Open ReVolt/Software
From EcoModder
The Open ReVolt Project: Wiki main page | Paul's website | Forum thread | Donate
svn repository (browse only)
Current Controller Software--ZeroGasoline 19:44, 21 May 2009 (EDT)
Contents |
Latest Software (cut and paste into avrstudio):
Create the 3 files listed in AVRStudio and add the code to the associated file.
Cougar.c
/*
firmware for cougar open source DC motor controller
*/
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/iom8.h>
#include <util/crc16.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cougar.h"
// now using automatically generated CRC file
#include "autocrc.h"
typedef struct {
int throttle_ref;
int current_ref;
int current_fb;
unsigned raw_hs_temp;
unsigned raw_throttle;
} realtime_data_type;
typedef struct {
long K1;
long K2;
long error_new;
long error_old;
long pwm;
} pi_storage_type;
typedef struct {
unsigned magic; // must be 0x12ab
int Kp; // PI loop proportional gain
int Ki; // PI loop integreal gain
unsigned throttle_min_raw_counts; // throttle low voltage (pedal to metal)
unsigned throttle_max_raw_counts; // throttle high voltage (foot off pedal)
unsigned throttle_pos_gain; // gain for actual throttle position
unsigned throttle_pwm_gain; // gain for pwm (voltage)
int current_ramp_rate; // current ramp rate
int spare[7]; // spare parameters
unsigned crc; // checksum for verification
} config_type;
config_type default_config PROGMEM = {
0x12ab, // magic
2, // PI loop P gain (Joe's was 1)
160, // PI loop I gain (Joe's was 20)
413, // throttle low voltage (pedal to metal)
683, // throttle high voltage (foot off pedal)
8, // throttle pedal position gain
0, // throttle pwm (voltage) gain
6, // current ramp rate (from throttle)
};
unsigned char counter_16k = 0;
unsigned char counter_8k = 0;
unsigned char counter_4k = 0;
unsigned char ad_channel = 0;
unsigned char oc_cycles_off_counter = 0;
unsigned char in_pi_loop = 0;
volatile unsigned char pi_run_tm = 0;
volatile unsigned counter_1k = 0; // 1KHz (976Hz to be exact) counter for timing
volatile unsigned raw_current_fb; // AD channel 2
volatile unsigned raw_hs_temp; // AD channel 1
volatile unsigned raw_throttle; // AD channel 0
unsigned vref = 0; // zero current voltage for LEM current sensor
unsigned ocr1a_lpf = 0; // ocr1a run through lowpass filter (sort of averaged)
unsigned max_current_ref = MAX_CURRENT_REF; // max_current_ref in variable so controlled by temperature
int throttle_ref = 0; // reference (desired) throttle
int current_ref = 0; // reference (desired) current
int current_fb = 0; // current feedback (actual current)
unsigned long idle_loopcount; // how many loops we do while micro is not executing PI
pi_storage_type pi;
config_type config;
realtime_data_type rt_data;
#ifdef crc_address
// calc CRC for program (firmware)
unsigned int calc_prog_crc(unsigned nbytes)
{
unsigned n, crc;
crc = 0xffff;
for (n = PROGSTART; n < nbytes; n++) {
crc = _crc_ccitt_update (crc, pgm_read_byte(n));
}
return(crc);
}
#endif
// calc CRC on block in SRAM
unsigned int calc_block_crc(unsigned nbytes, unsigned char *buf)
{
unsigned n, crc;
crc = 0xffff;
for (n = 0; n < nbytes; n++) {
crc = _crc_ccitt_update (crc, *(buf + n));
}
return(crc);
}
inline void clear_oc(void)
{
PORTB &= ~PB_OC_CLEAR; // OC clear low (low to clear)
asm("nop"); asm("nop"); // 4 nops = 1/4th uS - enough for 74HC00
asm("nop"); asm("nop");
PORTB |= PB_OC_CLEAR; // OC clear high (high for normal operation)
}
inline unsigned get_time(void)
{
unsigned t;
cli(); t = counter_1k; sei();
return(t);
}
inline unsigned diff_time(unsigned before)
{
unsigned now;
cli(); now = counter_1k; sei();
return(now - before);
}
unsigned long wait_time(unsigned howlong)
{
unsigned begin;
unsigned long loopcount;
loopcount = 0;
begin = get_time();
while (diff_time(begin) < howlong) loopcount++;
return(loopcount);
}
// PI loop code - runs at 4Khz
void pi_loop(void)
{
static unsigned char throttle_counter = 0;
unsigned char entry_tm, exec_tm;
unsigned loc_current_fb, loc_throttle;
unsigned uv1, uv2;
int i;
entry_tm = counter_16k;
loc_current_fb = raw_current_fb;
loc_throttle = raw_throttle;
sei();
// now we have a snapshot of all ADC readings and interrupts are enabled
// the timer 1 overflow ISR should re-enter on itself if it needs to
// we also have a snapshot of the entry time (16KHz counter) so we can measure execution time
// convert loc_current_fb from raw value to scaled (0 to 511)
// current starts in [512, 512 + 213] (if it's in 0 to 500 amps for the LEM 300)
if (loc_current_fb < vref) loc_current_fb = 0;
else loc_current_fb -= vref;
// now current is in the range [0, 213] or so
current_fb = (loc_current_fb * 19) >> 3; // (19/8 is almost 2.4)
// now current is in [0, 506] or so, close to same as current reference range
pi.error_new = current_ref - current_fb;
// execute PI loop
// first, K1 = Kp << 10;
// second, K2 = Ki - K1;
if (current_ref == 0) {
pi.pwm = 0;
//pi.Kp = 0;
//pi.Ki = 0;
// if Kp and Ki = 0, then K1 and K2 = 0;
// if K1 and K2 = 0, then pwm = pwm + 0, and 0 + 0 = 0
// so we don't need to run the PI loop, just set error_old to error_new
}
else {
pi.pwm += (pi.K1 * pi.error_new) + (pi.K2 * pi.error_old);
}
pi.error_old = pi.error_new;
if (pi.pwm > (510L << 16)) pi.pwm = (510L << 16);
else if (pi.pwm < 0L) pi.pwm = 0L;
if (pi.pwm & 0x8000) OCR1A = (pi.pwm >> 16) + 1;
else OCR1A = (pi.pwm >> 16);
// calculate average OCR1A value
// OCR1A max value is 511, so we can multiply it by up to 127 times
ocr1a_lpf = ((ocr1a_lpf * 15) + (unsigned)OCR1A) >> 4;
throttle_counter++;
if ((throttle_counter & 0x03) == 0x00) {
// run throttle logic at 1KHz - calculate throttle_ref
if (loc_throttle > config.throttle_max_raw_counts)
loc_throttle = config.throttle_max_raw_counts;
else if (loc_throttle < config.throttle_min_raw_counts)
loc_throttle = config.throttle_min_raw_counts;
loc_throttle -= config.throttle_min_raw_counts;
// now loc_throttle is in [0, (throttle_max_raw_counts - throttle_min_raw_counts)]
loc_throttle = (config.throttle_max_raw_counts - config.throttle_min_raw_counts) -
loc_throttle;
/*
now, 0 throttle is 0,
and max throttle is (throttle_max_raw_counts - throttle_min_raw_counts)
*/
throttle_ref = (long)loc_throttle * (long)MAX_CURRENT_REF /
(long)(config.throttle_max_raw_counts - config.throttle_min_raw_counts);
// now throttle ref in [0 to 511]
}
else if ((throttle_counter & 0x03) == 0x01) {
// run throttle logic at 1KHz - calculate current_ref from throttle_ref
// throttle gain logic
uv1 = (throttle_ref * config.throttle_pos_gain) >> 8;
uv2 = (throttle_ref * config.throttle_pwm_gain) >> 8;
if (uv1 > uv2) loc_throttle = uv1 - uv2;
else loc_throttle = 0;
// current_ref ramp rate logic
if (loc_throttle > max_current_ref) i = (max_current_ref - current_ref);
else i = (int)loc_throttle - current_ref;
if (i > config.current_ramp_rate) i = config.current_ramp_rate;
else if (i < -config.current_ramp_rate) i = -config.current_ramp_rate;
current_ref += i;
}
// measure how long execution took
exec_tm = counter_16k - entry_tm;
if (exec_tm > pi_run_tm) pi_run_tm = exec_tm;
}
// TIMER1 overflow interrupt
// This occurs center aligned with PWM output - best time to sample current sensor
// Rate is 16KHz
ISR(TIMER1_OVF_vect)
{
unsigned ui;
counter_16k++;
if (counter_16k & 0x01) {
// every other time (8KHz)
counter_8k++;
if (counter_8k & 0x01) {
// conversion on throttle or heatsink done - grab result
ui = ADC;
ADMUX = ADMUX = (1 << REFS0) | 2; // start conversion on current fb chan
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADSC);
if (ad_channel == 0) raw_throttle = ui;
else if (ad_channel == 1) raw_hs_temp = ui;
counter_4k++;
if ((counter_4k & 0x03) == 0) counter_1k++; // 1 KHz counter for delays, etc.
// overcurrent trip logic
if (PINB & PINB_OC_STATE) {
// overcurrent circuit tripped
oc_cycles_off_counter++;
}
if (oc_cycles_off_counter >= NUM_OC_CYCLES_OFF) {
// time to reset overcurrent trip circuit
oc_cycles_off_counter = 0;
#ifdef OC_CLEAR_ENABLED
clear_oc();
#endif
}
}
else {
// convertion on current sensor reading complete (4KHz)
raw_current_fb = ADC; // get conversion result
ad_channel++; // next channel channel
if (ad_channel > 1) ad_channel = 0; // wrap around logic
ADMUX = ADMUX = (1 << REFS0) | ad_channel; // set channel and start conversion
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADSC);
// execute PI loop with re-entrancy check
if (!in_pi_loop) {
in_pi_loop = 1; pi_loop(); in_pi_loop = 0;
}
}
}
}
// timer 1 input capture ISR (1000 hertz)
SIGNAL(SIG_INPUT_CAPTURE1)
{
counter_1k++; // 1 KHz counter for delays, etc.
}
unsigned char measure_vref(void)
{
unsigned char lp;
unsigned sum;
sum = 0;
for (lp = 0; lp < 16; lp++) {
// do a conversion on channel 2 - current sensor
ADMUX = ADMUX = (1 << REFS0) | 2;
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADSC);
while (ADCSRA & (1 << ADSC));
sum += ADC;
}
vref = sum >> 4;
if ((vref > (512 + 50)) || (vref < (512 - 50))) return(0);
return(1);
}
void config_pi(void)
{
/* A couple of points:
We are now doing OCR1A = (pwm >> 16) instead of (pwm >> 15)
Because of this, both Kp and Ki must double for same loop response
Also, the PI loop is run at 4KHz instead of 16, so Ki must quadruple for same loop response
So for same loop response, Kp is 2X and Ki is 8X */
long v1, v2;
v1 = (long)config.Kp << 10;
v2 = (long)config.Ki - pi.K1;
cli(); pi.K1 = v1; pi.K2 = v2; sei();
}
void fetch_rt_data(void)
{
// fetch variable with interrupts off, then re-enable interrupts (interrupts can happen during NOPs)
cli(); rt_data.throttle_ref = (volatile int)throttle_ref; sei();
asm("nop"); asm("nop"); asm("nop"); asm("nop");
// fetch variable with interrupts off, then re-enable interrupts (interrupts can happen during NOPs)
cli(); rt_data.current_ref = (volatile int)current_ref; sei();
asm("nop"); asm("nop"); asm("nop"); asm("nop");
// fetch variable with interrupts off, then re-enable interrupts (interrupts can happen during NOPs)
cli(); rt_data.current_fb = (volatile int)current_fb; sei();
asm("nop"); asm("nop"); asm("nop"); asm("nop");
// fetch variable with interrupts off, then re-enable interrupts (interrupts can happen during NOPs)
cli(); rt_data.raw_hs_temp = (volatile unsigned)raw_hs_temp; sei();
asm("nop"); asm("nop"); asm("nop"); asm("nop");
// fetch variable with interrupts off, then re-enable interrupts (interrupts can happen during NOPs)
cli(); rt_data.raw_throttle = (volatile unsigned)raw_throttle; sei();
asm("nop"); asm("nop"); asm("nop"); asm("nop");
}
void read_config(void)
{
eeprom_read_block(&config, (void *)EE_CONFIG_ADDRESS, sizeof(config));
if (config.magic == 0x12ab) {
// magic OK
if (calc_block_crc(sizeof(config) - sizeof(unsigned), (unsigned char *)&config) ==
config.crc) {
// CRC ok
return;
}
}
memcpy_P(&config, &default_config, sizeof(config));
}
void write_config(void)
{
config.crc = calc_block_crc(sizeof(config) - sizeof(unsigned), (unsigned char *)&config);
eeprom_write_block(&config, (void *)EE_CONFIG_ADDRESS, sizeof(config));
}
void show_menu(char *str)
{
sprintf_P(str, PSTR("Cougar OS controller firmware v%d.%d\r\n"),
MAJOR_VERSION, MINOR_VERSION);
uart_putstr(str);
}
void show_config(char *str)
{
sprintf_P(str, PSTR("Kp=%d Ki=%d\r\n"), config.Kp, config.Ki);
uart_putstr(str);
sprintf_P(str, PSTR("throttle_min_raw_counts=%u throttle_max_raw_counts=%u\r\n"),
config.throttle_min_raw_counts, config.throttle_max_raw_counts);
uart_putstr(str);
sprintf_P(str, PSTR("throttle_pos_gain=%u throttle_pwm_gain=%u\r\n"),
config.throttle_pos_gain, config.throttle_pos_gain);
uart_putstr(str);
sprintf_P(str, PSTR("current_ramp_rate=%d\r\n"), config.current_ramp_rate);
uart_putstr(str);
}
int main(void)
{
int x;
unsigned tm_show_data;
unsigned char cmdpos, cmdok;
char cmd[32];
char str[80];
#ifdef crc_address
unsigned crc1, crc2;
crc1 = pgm_read_word(crc_address); // read program CRC
crc2 = calc_prog_crc(crc_address); // read program CRC
if (crc1 != crc2) {
// program CRC error
while(1); // do nothing for ever
}
#endif
wdt_enable(WDTO_250MS); // enable watchdog
PORTD = 0xff & ~PD_LED & ~PD_CONTACTOR; // PORTD weak pullups, LED output pins low (LEDs off)
DDRD = PD_LED & PD_CONTACTOR; // two pins outputs
PORTC = ~PC_ANALOGS_USED; // weaks pull ups on _except_ for analog input pins
PORTB = 0xff & ~PB_PWM; // PWM output low, other outputs high, weak pullups on
DDRB = PB_PWM | PB_OC_CLEAR; // two pins outputs
// External Vcc (5v) for analog reference
ADMUX = (1 << REFS0);
// enable ADC, prescale = 128, so conversion clock is 16M / 128 = 125KHz
// a conversion take 13 cycles, at 125KHz equals 104uS
// the fastest we can convert is 9.6 KHz
// if we convert every other PWM cycle, that is 8KHz
// see page 198 of ATMEG8 manual
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
// do a conversion on channel 2 - the first conversion takes 25 cycles - so do it now
ADMUX = ADMUX = (1 << REFS0) | 2;
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADSC);
while (ADCSRA & (1 << ADSC));
// set up input capture 1 interrupt at 976Hz
// this is only for temporary timing until timer 1 is used for PWM
// the reason for 976Hz instead of 1000Hz is explained below
TCNT1 = 0; // load 16 bit counter 1
ICR1 = (long)F_OSC / 976; // timer at 976 Hz
TCCR1A = 0; // no output action on match
// let counter 1 run at fosc, reset to 0 at ICR1
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
TIMSK = (1 << TICIE1); // enable input capture 1 interrupt
read_config(); // read config from EEprom
config_pi(); // configure PI loop from config structure
// interrups are now enabled by config_pi() - sei() instruction in config_pi()
idle_loopcount = wait_time(100); // wait 100mS and remember how many loops we did
wdt_reset(); // kick watchdog
// now that voltages have settled, measure Vref
if (!measure_vref()) {
// vref out of range
while(1) {
wdt_reset(); // kick watchdog
}
}
// clear overcurrent fault (powerup in unknown state)
clear_oc();
// now configure timer 1 for PWM
TIMSK = 0; // no timer 1 interrupt
TCCR1B = 0; // stop counter 1
TCNT1 = 0; // load 16 bit counter 1
OCR1A = 0; // set initial PWM duty value to 0
TCCR1A = (1 << COM1A1) | (1 << WGM11); // Pase Correct PWM mode, 9 bit
TCCR1B = (1 << CS10); // Pre-scaler = 1
OCR1A = 0; // again, just to be safe
TIMSK = (1 << TOIE1); // enable overflow 1 interrupt
// now the PWM frequency = 16000000 / (1 << 9) / 2
// so PWM frequency = 16000000 / 1024 = 15625Hz
// now, counter_1k is incremented every 16 interrupt, so 15625 / 16 = 976.5625Hz
// this is why we run SIG_INPUT_CAPTURE1 at 976Hz
setup_uart(19200, PARITY_NONE, BITS_8_1); // uart 19200,n,8,1
show_menu(str); // might as well
// init some time variables
tm_show_data = get_time();
// now listen on serial port for commands
memset(cmd, 0, sizeof(cmd)); cmdpos = 0;
while (1) {
wdt_reset();
x = uart_getch();
if (x >= 0) {
if (x != 0x0d) {
// not a CR
uart_putch(x); // echo the character back
if (cmdpos < (sizeof(cmd) - 1)) {
cmd[cmdpos++] = x; // add character to command string
cmd[cmdpos] = 0; // and terminate command string
}
}
else {
// got a CR
uart_putch(0x0a); uart_putch(0x0d); // echo back LF and CR
cmdok = 0;
if (cmdpos > 0) {
for (x = 0; x < (int)(cmdpos - 1); x++) {
if (cmd[x] == ' ') {
// have a space character, terminate at this position
// and get numeric value
cmd[x] = 0; x = atoi(&cmd[x + 1]); cmdok = 1;
}
}
}
if (cmdok) {
// cmd is string, x is numeric value
if (!strcmp_P(cmd, PSTR("save"))) {
write_config();
sprintf_P(str, PSTR("configuration written to EE\r\n"));
uart_putstr(str);
}
else if (!strcmp_P(cmd, PSTR("idle"))) {
sprintf_P(str, PSTR("AVR %lu%% idle\r\n"),
wait_time(100) * (long)100 / idle_loopcount);
uart_putstr(str);
}
else if (!strcmp_P(cmd, PSTR("kp"))) {
if ((unsigned)x <= 500) {
config.Kp = x; config_pi();
show_config(str);
}
}
else if (!strcmp_P(cmd, PSTR("ki"))) {
if ((unsigned)x <= 500) {
config.Ki = x; config_pi();
show_config(str);
}
}
else if (!strcmp_P(cmd, PSTR("t-min-rc"))) {
if ((unsigned)x <= 1023) {
cli(); config.throttle_min_raw_counts = x; sei();
show_config(str);
}
}
else if (!strcmp_P(cmd, PSTR("t-max-rc"))) {
if ((unsigned)x <= 1023) {
cli(); config.throttle_max_raw_counts = x; sei();
show_config(str);
}
}
else if (!strcmp_P(cmd, PSTR("t-pos-gain"))) {
if ((unsigned)x <= 128) {
cli(); config.throttle_pos_gain = x; sei();
show_config(str);
}
}
else if (!strcmp_P(cmd, PSTR("t-pwm-gain"))) {
if ((unsigned)x <= 128) {
cli(); config.throttle_pwm_gain = x; sei();
show_config(str);
}
}
else if (!strcmp_P(cmd, PSTR("c-rr"))) {
if ((unsigned)x <= 100) {
cli(); config.current_ramp_rate = x; sei();
show_config(str);
}
}
}
else show_menu(str);
// reset command string
cmdpos = 0; cmd[0] = 0;
}
}
/* add non time-critical code below */
fetch_rt_data();
if (diff_time(tm_show_data) >= 200) {
// 200mS passed since last time, adjust tm_show_data to trigger in 200mS again
tm_show_data += 200;
sprintf_P(str, PSTR("TR=%03d CR=%03d CF=%03d HS=%04u RT=%04u\r\n"),
rt_data.throttle_ref, rt_data.current_ref, rt_data.current_fb,
rt_data.raw_hs_temp, rt_data.raw_throttle);
uart_putstr(str);
}
}
return(0);
}
Cougar.h
#define MAJOR_VERSION 0 #define MINOR_VERSION 1 #define F_OSC 16000000 // oscillator-frequency in Hz #define PROGSTART 0x0000 // program start address #define EE_CONFIG_ADDRESS 0 // address of config in EEprom #define OC_CLEAR_ENABLED // defin to enable AVR to clear OC fault #define NUM_OC_CYCLES_OFF 4 // number of overcurrent cycles off (at 4KHz) #define MAX_CURRENT_REF 511 // max current ref into PI loop #define PINB_OC_STATE (1 << PINB0) // OC state (high means fault) #define PB_PWM (1 << PB1) // PWM output pin (high to turn FETs on) #define PB_OC_CLEAR (1 << PB2) // OC clear (low to clear, high for normal operation) #define PD_LED (1 << PD6) // IDLE LED - high to light LED #define PD_CONTACTOR (1 << PD7) // Contactor Opto LED - high to light LED // three analog inputs used, these pins must be inputs and weak pullups off #define PC_ANALOGS_USED ((1 << PC0) | (1 << PC1) | (1 << PC2)) #define PARITY_NONE 0x00 #define PARITY_EVEN 0x02 #define PARITY_ODD 0x03 #define BITS_7_1 0x02 #define BITS_7_2 0x06 #define BITS_8_1 0x03 #define BITS_8_2 0x07 #define UART_RXBUF_SIZE 16 #define UART_TXBUF_SIZE 128 // function prototypes // get character from uart fifo, return -1 if fifo empty int uart_getch(void); // put character to uart (return 1 if fifo full, else 0) unsigned char uart_putch(char c); // put string to uart void uart_putstr(char *str); // set up UART to specified baud, parity, and bits void setup_uart(unsigned long baud, unsigned char parity, unsigned char bits);
Serial.c
/*
serial port support for cougar.c
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/iom8.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cougar.h"
// UART (serial)
typedef struct {
unsigned char rxbuf[UART_RXBUF_SIZE];
unsigned char txbuf[UART_TXBUF_SIZE];
unsigned rxhead;
unsigned rxtail;
unsigned txhead;
unsigned txtail;
} uart_fifo_type;
uart_fifo_type uart;
/* uart receive interrupt */
SIGNAL(SIG_UART_RECV)
{
unsigned char c;
unsigned i;
c = UDR;
i = uart.rxhead + 1;
if (i >= UART_RXBUF_SIZE) i = 0;
if (i != uart.rxtail) {
// fifo not full
uart.rxbuf[uart.rxhead] = c;
uart.rxhead = i;
}
}
/* uart UDR empty interrupt */
SIGNAL(SIG_UART_DATA)
{
unsigned i;
i = uart.txtail;
if (i != uart.txhead) {
UDR = uart.txbuf[i++];
if (i >= UART_TXBUF_SIZE) i = 0;
uart.txtail = i;
}
else {
// disable TX buffer empty interrupt
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
}
}
// get character from uart fifo, return -1 if fifo empty
int uart_getch(void)
{
unsigned char c;
unsigned i, j;
i = uart.rxtail;
cli(); j = uart.rxhead; sei();
if (i != j) {
c = uart.rxbuf[i++];
if (i >= UART_RXBUF_SIZE) i = 0;
cli(); uart.rxtail = i; sei();
return(c);
}
return(-1);
}
// put character to uart (return 1 if fifo full, else 0)
unsigned char uart_putch(char c)
{
unsigned i, j;
i = uart.txhead + 1;
if (i >= UART_TXBUF_SIZE) i = 0;
cli(); j = uart.txtail; sei();
if (i == j) {
// fifo full
return(1);
}
uart.txbuf[uart.txhead] = c;
cli(); uart.txhead = i; sei();
// enable TX buffer empty interrupt
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE) | (1 << UDRIE);
return(0);
}
// put string to uart
void uart_putstr(char *str)
{
char ch;
while (1) {
ch = *str++;
if (ch == 0) break;
while (uart_putch(ch));
}
}
// set up UART to specified baud, parity, and bits
void setup_uart(unsigned long baud, unsigned char parity, unsigned char bits)
{
unsigned int ubrr;
ubrr = (F_OSC / ((unsigned long)16 * baud)) - 1;
UBRRL = ubrr & 0xff;
UBRRH = ubrr >> 8;
UCSRC = (parity << 4) | (bits << 1) | (1 << URSEL);
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
}
--Adamj12b 19:30, 5 October 2009 (EDT)
