/********************************************* cavr64 (c)2006 by Doug Garmon Gnu Public Licence http://www.gnu.org/copyleft/gpl.html c64dtv<->AVR Protocol ********************************************** * Chip type : ATMEGA8 * Clock frequency : Internal clock 4 Mhz (lowfuse=0xE3) *********************************************/ #include #include #include #include #define F_CPU 4000000UL // 4 MHz #include #include #include #define BUTTON PB0 #define BUTTON_DD DDB0 #define ATTN PD0 #define CLK PD1 #define DATAMASK 28 /* right-shift 10-bit ADC values by these amounts for 9 or 8 bits */ #define RES_8 2 #define RES_9 1 #define MAXCHANNELS 5 // total channels - 1 : ATmega8 28pin //#define MAXCHANNELS 7 // total channels - 1 : ATmega16, others, 40pin /* std commands */ #define CMD_SEND_STATUS 0 #define CMD_USER1 1 #define CMD_SEND_ADCARRAY 2 #define CMD_SEND_KEYS 3 #define CMD_USER2 4 #define CMD_USER3 5 #define CMD_SEND_DATA 6 #define CMD_EXTENDED 7 /* extended commands */ #define EXT_NULL 0 #define EXT_RESET 1 #define EXT_HARDRESET 2 #define EXT_TEST 3 #define EXT_RESET_STATUS 4 #define EXT_ADCCHAN 10 // set single channel #define EXT_ADCARRAY 11 // set channel array #define EXT_ARRAY_MAXIMUM 12 // set # of channels #define EXT_DATA_NTERM 64 // set data sent to null-terminated #define EXT_DATA_BLK 65 // set data send to block #define EXT_DATA_LOADINFO 66 // get load addr/len #define EXT_DATA_NAME 67 // get filename #define EXT_TOGGLE_0 200 #define EXT_OUTPUT_RESET 216 /* config info for extended cmds */ #define CONFIG_NULL 0 #define CONFIG_DATA_NTERM 1 // null-terminated data #define CONFIG_DATA_BLK 2 // data in blocks #define CONFIG_STATUS_DEBUG 32 //------------------------------------------ // Global variables--BAD elsewhere, GOOD for uC uint8_t adcres = RES_9; // shift 10bit value this much--use 2 for 8bit, 1 for 9bit // only 9 bits can fit in a 3x3 transfer (a 10 bit routine should be written, tho) uint16_t extcmd = EXT_NULL; // extended cmd uint8_t config_data = CONFIG_DATA_NTERM; // type of data to send uint8_t config_status = CONFIG_STATUS_DEBUG; uint8_t adcarray[] = {5, 4, 3, 2, 1, 0, 0, 0}; // array of adc channels for a rolling read // 8 bytes for ATmega16, etc. (and 8L in 32 pin pkg.) uint8_t curchannel = 0; // array index of current channel uint8_t maxchannel = 1; // max # (-1) of array entries to use, 0 is ok (for single channel) //uint8_t inbuffer[16]; // input buffer uint16_t outdatalen = 32; // length of data block for output uint8_t loadlo = 0; uint8_t loadmid = 6; // load addr for data block (default = 1/2 way thru scrn memory) uint8_t loadhi = 0; // for flash loads uint8_t temp1, temp2; //------------------------------------------ void delay_ms(unsigned int ms) /* delay for a minimum of */ { // we use a calibrated macro. This is more // accurate and not so much compiler dependent // as self made code. while(ms){ _delay_ms(0.96); ms--; } } /*---------- LED functions---------*/ // LO means ON for LEDs, off for Relays void output_lo(uint8_t bmask) { PORTC &= ~_BV(bmask); } void output_hi(uint8_t bmask) { PORTC |= _BV(bmask); } /*--------------------------------------------------- BLINK functions are debugging only not part of the normal function ----------------------------------------------------*/ void blink(uint8_t mask) { output_lo(mask); delay_ms(40); output_hi(mask); delay_ms(40); } void blink_multi(uint8_t mask, uint8_t num ) { unsigned char j; for (j=0; j> 1); // ditto sendnip(byteplus >> 4); // again // if there is no pause here to wait for clock to drop, DTV doesn't have time // to read the last 'nip' because the port is switched back to input too quickly loop_until_bit_is_clear(PIND, CLK); // wait for CLK lo DDRD &= ~(_BV(DDD2) | _BV(DDD3) | _BV(DDD4)); // back to input } uint8_t getnip(void) { loop_until_bit_is_clear(PIND, CLK); // wait for CLK lo loop_until_bit_is_set(PIND, CLK); // wait for CLK hi return(PIND & DATAMASK); } // getbyte() -- does what it says // get 3x3 from master and return 9 bit number uint16_t getbyte(void) { uint16_t lbyte; // a 'long' byte -- 9 bits lbyte = getnip() >> 2; lbyte |= (getnip() << 1); lbyte |= (uint16_t)(getnip() << 4); return(lbyte); } // single ADC read, modified a routine by Peter Fleury http://jump.to/fleury // If IRQs are to used elseware, this should be changed to free-running mode-- // setting ADMUX to next channel at the end of the current read. void sendADC(uint8_t adcno) { ADMUX = adcno; // Select pin ADC0 using MUX ADCSRA = _BV(ADEN) | _BV(ADPS2); // Activate with Prescaler 16 --> 4Mhz/16 = 250kHz (little too high for 10bits, but not 9) ADCSRA |= _BV(ADSC); // Start sampling while (ADCSRA & _BV(ADSC) ) {} // wait sendbyte(ADCW >> adcres); // send it, while dumping the 10th bit } // send a null-terminated data string back to master void senddata_nt(char *data) { uint8_t bindex = 0; while(data[bindex]) { sendbyte((uint16_t)data[bindex]); bindex++; } sendbyte(0); } // send a data block back to master (64k limit) // too dumb to utilize extra bit :( void senddata(char *data, uint16_t len) { uint16_t i; for(i=0; i> 4; adcarray[i*2+1] = temp1 & 15; // do it w/nibbles (2 channels/byte) } break; // set # of cells in array to use before resetting to zero case EXT_ARRAY_MAXIMUM: maxchannel = (uint8_t)getbyte(); if (maxchannel > MAXCHANNELS) maxchannel = MAXCHANNELS; break; // get information about current data block // load addr, length // datalen = datalength - 1 (for c64 code) case EXT_DATA_LOADINFO: sendbyte((outdatalen-1) & 255); // len, LSB //sendbyte(16); // len, LSB sendbyte((outdatalen-1) >> 8); // len, MSB sendbyte(loadlo); // loadaddr, LSB sendbyte(loadmid); // loadaddr, MSB sendbyte(loadhi); // loadaddr, MSB flash break; // nuclear option--watchdog timer reset of AVR // 'ubernuclear' option--tie RESET pin to CASSENCE line :) case EXT_HARDRESET: resetavr(); break; // 'soft' reset of settings case EXT_RESET: // add reset of array values adcarray[0] = 5; curchannel = 0; maxchannel = 1; break; // reset output line(s) // reset is HIGH (open-collector) case EXT_OUTPUT_RESET: PORTC |= _BV(PC0); // leds -- hi is off break; // transmit null-term data case EXT_DATA_NTERM: config_data = CONFIG_DATA_NTERM; break; // transmit binary data case EXT_DATA_BLK: config_data = CONFIG_DATA_BLK; break; default: break; } } /*----------------------------------------------------------------------------------*/ int main(void) { uint8_t command; // current command char buffer[] = "THIS IS A NULL-TERMINATED CHAR STRING\0"; /* INITIALIZE */ /* enable PD0 - PD4 as input */ // clear bits, input DDRD &= ~(_BV(DDD0) | _BV(DDD1) | _BV(DDD2) | _BV(DDD3) | _BV(DDD4)); // set bit, enable pull-up resistor PORTD |= (_BV(PD0) | _BV(PD1) | _BV(PD2) | _BV(PD3) | _BV(PD4) ); /* PC2 button input */ DDRB &= ~_BV(BUTTON_DD); // clear bit, input PORTB |= _BV(BUTTON); // set bit, enable pull-up resistor /* indicator LEDs - PC0 & PC1 */ DDRC |= _BV(DDC0); // set bit, output PORTC |= _BV(PC0); // leds -- hi is off DDRC |= _BV(DDC1); // set bit, output PORTC |= _BV(PC1); // leds -- hi is off // blink once to indicate startup blink(PC1); while (1) { if(!(PIND & _BV(ATTN))) // if ATTN is LO { output_lo(PC1); // debug--LED ON when inside loop // master releases ATNN somewhere here (back to HI) loop_until_bit_is_set(PIND, ATTN); // wait for ATTN to go hi loop_until_bit_is_set(PIND, CLK); // wait for CLK hi switch (command = PIND >> 2) { case CMD_SEND_ADCARRAY: sendADC(adcarray[curchannel]); curchannel++; if (curchannel > maxchannel) curchannel = 0; break; case CMD_SEND_STATUS: sendbyte(extcmd); break; case CMD_SEND_DATA: if (config_data == CONFIG_DATA_NTERM) senddata_nt(buffer); else senddata(buffer,outdatalen); break; case CMD_SEND_KEYS: if (!(PINB & _BV(BUTTON))) sendbyte(1); else sendbyte(0); break; // read the incoming extended byte case CMD_EXTENDED: if ((extcmd = getbyte())) do_extcmd(); break; default: break; } } output_hi(PC1); // debug } return(0); }