Ett C-program exempel
Postat: 12 mars 2006, 20:43:42
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:
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.
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
}
}
}
}
}
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.