/* * ModularEEG firmware for one-way transmission, v0.5.5-p3 * Copyright (c) 2002-2003, Joerg Hansmann, Jim Peters, Andreas Robinson * Modification for Atmega48 (c) 2005, Yuri Smolyakov * License: GNU General Public License (GPL) v2 * Compiles with AVR-GCC v3.3. * * Note: -p3 in the version number means this firmware is for packet version 3. */ ////////////////////////////////////////////////////////////// /* * ModularEEG Packet Format Version 3 * * One packet can have zero, two, four or six channels (or more). * The default is a 6-channel packet, shown below. * * 0ppppppx packet header * 0xxxxxxx * * 0aaaaaaa channel 0 LSB * 0bbbbbbb channel 1 LSB * 0aaa-bbb channel 0 and 1 MSB * * 0ccccccc channel 2 LSB * 0ddddddd channel 3 LSB * 0ccc-ddd channel 2 and 3 MSB * * 0eeeeeee channel 4 LSB * 0fffffff channel 5 LSB * 1eee-fff channel 4 and 5 MSB * * Key: * * 1 and 0 = sync bits. * Note that the '1' sync bit is in the last byte of the packet, * regardless of how many channels are in the packet. * p = 6-bit packet counter * x = auxillary channel byte * a - f = 10-bit samples from ADC channels 0 - 5 * - = unused, must be zero * * There are 8 auxillary channels that are transmitted in sequence. * The 3 least significant bits of the packet counter determine what * channel is transmitted in the current packet. * * Aux Channel Allocations: * * 0: Zero-terminated ID-string (ASCII encoded). * 1: * 2: * 3: * 4: Port D status bits * 5: * 6: * 7: * * The ID-string is currently "mEEGv1.0". */ ////////////////////////////////////////////////////////////// /* * * Program flow: * * When 256Hz timer expires: goto SIGNAL(SIG_OVERFLOW0) * SIGNAL(SIG_OVERFLOW0) enables the ADC * * Repeat for each channel in the ADC: * Sampling starts. When it completes: goto SIGNAL(SIG_ADC) * SIGNAL(SIG_ADC) reads the sample and restarts the ADC. * * SIGNAL(SIG_ADC) writes first byte to UART data register * (UDR) which starts the transmission over the serial port. * * Repeat for each byte in packet: * When transmission begins and UDR empties: goto SIGNAL(SIG_UART_DATA) * * Start over from beginning. */ #include #include #include #include // Number of channels. 2, 4 or 6 #define NUMCHANNELS 6 // Sample frequency in Hz #define SAMPFREQ 256 #define HEADERLEN 2 #define PACKETLEN (3 * (NUMCHANNELS >> 1) + HEADERLEN) #define FOSC 7372800 // Clock Speed #define TIMER0VAL 256 - ((FOSC / 256) / SAMPFREQ) #define BAUD 57600 #define BAUDL FOSC/16/BAUD - 1 #define BAUDH (BAUDL)>>8 #if defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) #define SIG_UART_DATA SIG_USART_DATA #define ADCSR ADCSRA #define UCSRB UCSR0B #define UDRIE UDRIE0 #define UDR UDR0 #define TIMSK TIMSK0 #define TCCR0 TCCR0B #endif const char channel_order[]= {0, 3, 1, 4, 2, 5}; // The transmission packet uint8_t tx_buf[PACKETLEN]; // Next byte to read or write in the transmission packet register uint8_t tx_index asm("r3"); // ID string const char id_string[] = "mEEGv1.0"; // ID string index register uint8_t id_index asm("r4"); // Packet counter register uint8_t packet_cnt asm("r5"); // Current channel being sampled register uint8_t cur_channel asm("r6"); ///////////////////////////////////////////// // Sampling timer (timer 0) interrupt handler SIGNAL(SIG_OVERFLOW0) { outb(TCNT0, TIMER0VAL); //Reset timer to get correct sampling frequency cur_channel = 0; cbi(UCSRB, UDRIE); //Ensure UART IRQ's are disabled sbi(ADCSR, ADIF); //Reset any pending ADC interrupts sbi(ADCSR, ADIE); //Enable ADC interrupts //The ADC will start sampling automatically as soon //as sleep is executed in the main-loop. } /////////////////////////////////////////// // AD-conversion-complete interrupt handler SIGNAL(SIG_ADC) { register uint8_t i asm("r20"); register uint8_t lsb asm("r21"); register uint8_t msb asm("r22"); register uint8_t aux_data asm("r23"); lsb = inp(ADCL); // Read 8 least significant sample bits msb = inp(ADCH); // Read 2 most significant sample bits // The assembler code below does the same as // msb = ((msb << 1) | (lsb >> 7)) & 0x3; // lsb = lsb & 0x7F; // but in a prettier way // The end result is // msb = 3 most significant bits of sample, unused bits clear // lsb = 7 least significant bits of sample, unused bit clear asm volatile ( " rol %0" "\n" " rol %1" "\n" " lsr %0" "\n" " andi %1,7" "\n" : "=r" (lsb), "=r" (msb) : "r" (lsb), "r" (msb) ); i = (cur_channel >> 1); i = i+i+i + HEADERLEN; // i = 3*i+2, but without multiplication if (cur_channel & 1) { // Store odd channel tx_buf[i+1] = lsb; tx_buf[i+2] |= msb; } else { // Store even channel tx_buf[i] = lsb & 0x7F; tx_buf[i+2] = msb << 4; } cur_channel++; if (cur_channel < NUMCHANNELS) { outb(ADMUX, channel_order[cur_channel]); //Select the next channel return; //The next sampling is started automatically } outb(ADMUX, channel_order[0]); //Prepare next conversion, on channel 0 // Disable ADC interrupts to prevent further calls to SIG_ADC cbi(ADCSR, ADIE); // Write the packet heade. switch (packet_cnt & 7) { case 0: // Get the next character in the ID string aux_data = id_string[id_index++]; if (aux_data == 0) id_index = 0; break; case 4: // Get the state of switches on PD2..5, if any aux_data = (inp(PIND) >> 2) & 0x0F; break; default: aux_data = 0; } // Store the header tx_buf[0] = ((packet_cnt << 1) & 0x7E) | (aux_data >> 7); tx_buf[1] = aux_data & 0x7F; // Set the sync bit tx_buf[PACKETLEN-1] |= 0x80; packet_cnt++; // Hand over to SIG_UART_DATA, by starting // the UART transfer and enabling UDR IRQ's outb(UDR, tx_buf[0]); sbi(UCSRB, UDRIE); tx_index = 1; } ////////////////////////////////////////////////////////// // UART data transmission register-empty interrupt handler SIGNAL(SIG_UART_DATA) { outb(UDR, tx_buf[tx_index]); //Send next byte tx_index++; if (tx_index == PACKETLEN) { //See if we're done with this packet cbi(UCSRB, UDRIE); //Disable SIG_UART_DATA interrupts //Next interrupt will be a SIG_OVERFLOW0 } } //////////////////////////////////////////////////////// // Initialize PWM output (PB1 = 14Hz square wave signal) void pwm_init(void) { // Set timer/counter 1 to use 10-bit PWM mode. // The counter counts from zero to 1023 and then back down // again. Each time the counter value equals the value // of OCR1(A), the output pin is toggled. // The counter speed is set in TCCR1B, to clk / 256 = 28800Hz. // Effective frequency is then clk / 256 / 2046 = 14 Hz #if defined (__AVR_ATmega8__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) outb(OCR1AH, 2); // Set OCR1A = 512 outb(OCR1AL, 0); outb(TCCR1A, BV(COM1A1) + BV(WGM11) + BV(WGM10)); // Set 10-bit PWM mode outb(TCCR1B, (1 << CS12)); // Start and let run at clk / 256 Hz #else // __AVR_AT90S4434__ outb(OCR1H,2); // Set OCR1 = 512 outb(OCR1L,0); outb(TCCR1A, BV(COM11) + BV(PWM11) + BV(PWM10)); // Set 10-bit PWM mode outb(TCCR1B, (1 << CS12)); // Start and let run at clk / 256 Hz #endif } //////////////// // Initialize uC void init(void) { //Set up the ports outb(DDRD, 0xC2); outb(DDRB, 0x07); outb(PORTD, 0xFF); outb(PORTB, 0xFF); //Select sleep mode = idle #if defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) outb(SMCR,(inp(SMCR) | BV(SE)) & (~BV(SM0) | ~BV(SM1) | ~BV(SM2))); #elif defined (__AVR_ATmega8__) outb(MCUCR,(inp(MCUCR) | BV(SE)) & (~BV(SM0) | ~BV(SM1) | ~BV(SM2))); #else // __AVR_AT90S4434__ outb(MCUCR,(inp(MCUCR) | BV(SE)) & (~BV(SM))); #endif //Initialize the ADC // Timings for sampling of one 10-bit AD-value: // prescaler > ((XTAL / 200kHz) = 36.8 => // prescaler = 64 (ADPS2 = 1, ADPS1 = 1, ADPS0 = 0) // ADCYCLE = XTAL / prescaler = 115200Hz or 8.68 us/cycle // 14 (single conversion) cycles = 121.5 us (8230 samples/sec) // 26 (1st conversion) cycles = 225.69 us outb(ADMUX, 0); //Select channel 0 //Prescaler = 64, free running mode = off, interrupts off outb(ADCSR, BV(ADPS2) + BV(ADPS1)); sbi(ADCSR, ADIF); //Reset any pending ADC interrupts sbi(ADCSR, ADEN); //Enable the ADC // Analog comparator OFF outb(ACSR, BV(ACD)); //Initialize the UART #if defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) outb(UBRR0H, BAUDH); //Set speed to BAUD bps outb(UBRR0L, BAUDL); outb(UCSR0A, 0); outb(UCSR0C, BV(UCSZ01) | BV(UCSZ00)); outb(UCSR0B, BV(TXEN0)); //Enable transmitter #elif defined (__AVR_ATmega8__) outb(UBRRH, BAUDH); //Set serial port speed outb(UBRRL, BAUDL); outb(UCSRA, 0); outb(UCSRC, BV(URSEL) | BV(UCSZ1) | BV(UCSZ0)); outb(UCSRB, BV(TXEN)); #else // __AVR_AT90S4434__ outb(UBRR, BAUDL); // Set serial port speed outb(UCSRB, BV(TXEN)); #endif //Initialize timer 0 outb(TCNT0, 0); // Clear it outb(TCCR0, 4); // Start it. Frequency = clk / 256 outb(TIMSK, 1 << TOIE0); // Enable the interrupts } //////////////// int main(void) { packet_cnt = 0; id_index = 0; // Initialize init(); //Initialize PWM (optional) pwm_init(); sei(); // Now, we wait // This is an event-driven program, // so nothing much happens here while (1) { __asm__ __volatile__ ("sleep"); // sleep until something happens } }