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.