Ett C-program exempel

Elektronik- och mekanikrelaterad mjukvara/litteratur. (T.ex schema-CAD, simulering, böcker, manualer mm. OS-problem hör inte hit!)
Användarvisningsbild
Icecap
Inlägg: 26153
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Ett C-program exempel

Inlägg av Icecap »

Jag vill, i självförhärligande ändamål och för att visa vilken bror duktig jag är, lägga in ett trevligt program jag har gjort. Det är programmet som styr en PIC16F628A som kan styra 13 RC-servos. Jag jobbar på min hemsida och ska lägga in schema där. Programmet har jag försökt att kommentera så mycket som möjligt och då jag uteslutande programmera på engelska är det såklart med engelska kommentarer! Here it goes:

Kod: Markera allt

// RC-servo controller
// © Icecap 2006-03-11
// Recieves commands through the serial port and sends out the appropriate datas om the ports
// Protocoll:
// STX Channel Data ETX (spaces left for clarity only)
// Channel: '0' - '9','A'-'D'
// Data:    '0' - '999'

// Processor:
// PIC16F628A using INTOSC with NOCLKOUT

typedef unsigned char byte;
typedef unsigned int  word;
typedef unsigned long dword;

#define true  1
#define false 0

#define STX 0x02
#define ETX 0x03

#define Channels 13
#define INPUT_SIZE 5

// A few constant definitions
#define Offset  -970
#define Start_Value Offset - 499
// The 'Offset' is negative due to the fact that Timer1 counts up an gives a interrupt at overflow
// thus all times are calculated by subtracting them from 0 (zero) - some compensation för code time
// and then subtract the pulse length from the timer value. This means that the value 'Offset' represents
// equals the shortest pulse length possible (1ms).

union
  {
  word W;
  byte B[2];
  } Datas[Channels]; // This allow me to access BOTH as a unsigned int AND as 2 bytes/word
char Input[INPUT_SIZE]; // Room for the incoming data
byte Input_Index; // Input counter and pointer

// The follwing patterns is to select the correct pin on the µC
const byte PatternA[] = {0x01,0x02,0x04,0x08,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
const byte PatternB[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x08,0x10,0x20,0x40,0x80};

void Initialize(void)
  {
  OPTION_REG  =  0x87; // Well...stuff!
  CMCON       =  0x07; // No comparator-thingy
  // Set port directions
  TRISA       = 0b00100000; // All out but -MCLR
  TRISB       = 0b00000010; // All out but Serial-in
  // Set port values
  PORTA       = 0x00; // Start value
  PORTB       = 0x00; // Start value
  // Set known good values
  for(Input_Index = 0;Input_Index < Channels;Input_Index++) Datas[Input_Index].W = Start_Value; // Set all to center (1,000ms)
  // Allow interrupts
  PIE1        = 0b00000001; // Allow Timer1 interrupts
  INTCON      = 0b11000000; // Allow interrupts as such
  // Set UART to recieve only, no interrupts
  TXSTA       = 0b00000100; // BRGH = '1'
  RCSTA       = 0b10010000; // Enable continous recieve and clear errors
  SPBRG       = 25; // 9K6,n,8,1
  // Start Timer1 on the highest freq.
  T1CON       = 0b00000001;
  // Now set some working values
  Input_Index = 0;
  }

void interrupt(void)
  {
  static byte Sequence, Index, TimeH, TimeL, Pattern_PortA, Pattern_PortB; // Used here only and must be "locked"
  // It's Timer1 overflow (only enabled interrupt), it's a ISR with external interface so be snappy!
  PIR1.TMR1IF   = false;             // Acknowledge the interrupt
  T1CON.TMR1ON  = false;             // Stop the timer
  TMR1H         = TimeH;             // Set pulse time, high byte
  TMR1L         = TimeL;             // Set pulse time, low byte
  T1CON.TMR1ON  = true;              // Start timer again
  PORTA         = Pattern_PortA;     // Rise correct port pin
  PORTB         = Pattern_PortB;     // Rise correct port pin
  // Now the timing is less panic as the external job is done for now
  if(++Index >= Channels) Index = 0; // Count up and around
  TimeH         = Datas[Index].B[1]; // Prepare the values for faster access
  TimeL         = Datas[Index].B[0]; // Prepare the values for faster access
  Pattern_PortA = PatternA[Index];   // Prepare the values for faster access
  Pattern_PortB = PatternB[Index];   // Prepare the values for faster access
  }

void main(void)
  {
  byte Read;
  word Value;
  byte Digit;
  Initialize();
  while(true)
    {
    if(RCSTA & 0b00000110) // Any error exist?
      {
      RCSTA     = 0b10010000; // Erase any errors
      PIR1.RCIF = false;      // Remove it just in case
      }
    if(PIR1.RCIF) // Is there any char in UART?
      { // Char was recieved!
      Read      = RCREG; // Get the latest incoming byte
      PIR1.RCIF = false; // Got it, next please
      switch(Read) // Now select what to do with the incoming
        {
        case STX: // It's a STX = Start of message block, erase all other
          Input_Index = 0; // Start from the beginning!
        break; // Done with STX
        case ETX: // Block recieved, start treating the data
          if((((Input[0] >= '0') && (Input[0] <= '9')) || ((Input[0] >= 'A') && (Input[0] <= 'D'))) && (Input_Index > 1))
            { // Address is correct & valid
            Value = 0; // Start point
            Read  = 1; // Input[0] = address, [1->] = data
            while(--Input_Index && ((Input[Read] >= '0') && (Input[Read] <= '9'))) // Convert incoming while it's legal
              {
              Digit  = Input[Read++] - '0'; // Convert the read char from '0'-'9' to 0-9 and points on next
#if false
              Value *= 10; // Multiply the previous result by 10
#else // A faster way of doing 10*
              Value = ((Value << 2) + Value) << 1;
#endif
              Value += Digit; // Add the new ones
              }
            if(Value < 1000) // Is the value legal?
              { // Only if it's legal value (0-999)
              if(Input[0] > '9') Read = (Input[0] - 'A') + 10; // Convert address as hex
              else               Read =  Input[0] - '0'; // Or as decimal
              Value = Offset - Value; // Convert to the correct offset
              Datas[Read].W = Value; // And save in RAM-buffer for automatic retrieval by the Timer1 ISR
              }
            Input_Index = 0; // Done with that input, no reuse
            }
        break;
        default:
          if(Input_Index < INPUT_SIZE) // Test if buffer is full
            { // It's OK, room exists
            Input[Input_Index++] = Read; // Put in buffer and point on next
            }
        }
      }
    }
  }
Sådär ja, lite interrupt osv. Hoppas att det kan vara en hjälp för dom som vill in i den underbara C-värld.

Hårdvaran: Jag har en PIC16F628A, använder INTOSC NO CLKOUT, en transistor + motstånd mellan RS232 och PIC (duger helfint med en MAX232-grej) och sedan framgår resten av programmet.

Edit: Kompileras i MikroC.
Användarvisningsbild
manw
Inlägg: 207
Blev medlem: 16 november 2005, 11:16:17
Ort: Södermalm

Kompilatorn MikroC?

Inlägg av manw »

Hej

Ja, det blev verkligen lättare att programmera PIC-processorer i C istället för RISC-Assembler där en och annat komando verkar vara valt för att djävlas med programmeraren.

Jag har använt C-kompliatorn Cc5x till kretsarna PIC16F628 och PIC12F629 hittills. http://www.bknd.com/

Är lite nyfiken på den du nämner "MikroC", vad är det för pris på den, finns gratisversioner, i så fall med vilka begränsningar? För vilka plattformar? (OS).
Användarvisningsbild
MadModder
Co Admin
Inlägg: 30024
Blev medlem: 6 september 2003, 13:32:07
Ort: MadLand (Enköping)
Kontakt:

Inlägg av MadModder »

Alltså... är det någon som är tankeläsare? :D
Iofs hade jag tänkt knåpa med en liten 8-pinnars, och väldigt långsam rörelse på servona, typ 30°/s...
Användarvisningsbild
Icecap
Inlägg: 26153
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Inlägg av Icecap »

MikroC download

Den versionen har dock ett fel som berör just PIC16F628A! Mikroelektronika är informerat och har justerat filerna till nästa version.

I filen 'P16F628A.mlk' ska "#pragma SetLib(EEPROMlib_E_A, usartlib_U_E, RS485_M_S, Flash_F_E)" ändras till "#pragma SetLib(EEPROMlib_E_A, usartlib_U_E, RS485_M_S, Flash_F_E)"

Skillnaden är alltså att "RS485_M_E" ska ändras till "RS485_M_S".
Skriv svar