/* gadgetboard.c Serial command-line interface for Gadgetboard 1.0, a Free microcontroller board designed to make it easy to build a wide variety of electromechanical gadgets. Portions taken from Pascal Stang's GPL AVRLIB library Jason E. Holt Copyright (C) 2005 This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //----- Include Files --------------------------------------------------------- #include // include I/O definitions (port names, pin names, etc) #include #include #include "global.h" // include our global settings #include "uart.h" // include uart function library #include "rprintf.h" // include printf function library #include "timer.h" // include timer function library (timing, PWM, etc) #include "a2d.h" // include A/D converter function library //#include "vt100.h" // include VT100 terminal support #include "cmdline.h" // command line style serial interaction //----- Begin Code ------------------------------------------------------------ // // callbacks for cmdline code void reada2d(void); void setpwm(void); void setall(void); void setout(void); void help(void); void echo(void); // actual functions void setpwm_(int chan, int dc); void setout_(int out, int val); void setall_(char *val); int reada2d_(int pin); void inita2d(void); void mainloop(void); #define RED (0) #define YELLOW (1) #define GREEN (2) #define DELAYLOOPLEN (800000); int light1; int light2; int light1out[3] = { 1,2,3 }; int light2out[3] = { 5,6,7 }; void mydelay(int deciseconds) { int i; for( ; deciseconds > 0; deciseconds--) { for(i=0; i<100 ; i++) delay(1000); } } void safetycheck(void) { if((light1 != RED) && (light2 != RED)) { // Something's weird; go to failsafe red-blinking mode while(1) { setall_("00000000"); mydelay(3); setout_(light1out[RED], 1); setout_(light2out[RED], 1); mydelay(3); } } } void setoutputs(void) { setall_("00000000"); setout_(light1out[light1], 1); setout_(light2out[light2], 1); } int setlights(int l1, int l2, int d) { light1 = l1; light2 = l2; safetycheck(); setoutputs(); mydelay(d); } void lights(void) { while(1) { setlights(GREEN, RED, 20); setlights(YELLOW, RED, 7); setlights(RED, RED, 2); setlights(RED, GREEN, 30); setlights(RED, YELLOW, 9); setlights(RED, RED, 3); if(reada2d_(1) > 128) return; // Break out of stoplight mode } } // Unfortunately Pascal doesn't publicly support his library or accept // patches. So we have to include this code (which I wrote) here void timer0PWMInit(void) { // configures timer0 for use with PWM output // on OC0 pin TCCR0 &= ~_BV(WGM01); TCCR0 |= _BV(WGM00); // clear output compare value A outb(OCR0, 0); } void timer0PWMOff(void) { // turn off timer1 PWM mode cbi(TCCR0,WGM11); cbi(TCCR0,WGM10); cbi(TCCR0,COM01); cbi(TCCR0,COM00); } void timer0PWMOn(void) { // set OC1A as non-inverted PWM TCCR0 &= ~_BV(COM00); TCCR0 |= _BV(COM01); } void timer0PWMSet(u16 pwmDuty) { // this PWM output is generated on OC0 pin // NOTE: pwmDuty should be in the range 0-255 for 8bit PWM //outp( (pwmDuty>>8), OCR1AH); // set the high 8bits of OCR1A //outp( (pwmDuty&0x00FF), OCR1AL); // set the low 8bits of OCR1A OCR0 = pwmDuty; } void timer2PWMInit(void) { // configures timer2 for use with PWM output // on OC2 TCCR2 &= ~_BV(WGM21); TCCR2 |= _BV(WGM20); // clear output compare value A outb(OCR2, 0); } void timer2PWMOff(void) { // turn off timer1 PWM mode cbi(TCCR2,WGM21); cbi(TCCR2,WGM20); cbi(TCCR2,COM21); cbi(TCCR2,COM20); } void timer2PWMOn(void) { // set OC2 as non-inverted PWM TCCR2 &= ~_BV(COM20); TCCR2 |= _BV(COM21); } void timer2PWMSet(u16 pwmDuty) { // this PWM output is generated on OC2 pin // NOTE: pwmDuty should be in the range 0-255 for 8bit PWM OCR2 = pwmDuty; } void initports(void) { // set the PCx port pins to output sbi(DDRC, PC0); sbi(DDRC, PC1); sbi(DDRC, PC6); sbi(DDRC, PC7); // set the OCx port pins to output // We need to do this so we can see and use the PWM signal // Different processors use different pins for this sbi(DDRB, PB3); sbi(DDRD, PD4); sbi(DDRD, PD5); sbi(DDRD, PD7); PORTC = 0; PORTD &= _BV(PD4|PD5|PD7); PORTB &= _BV(PB3); } void initpwm(void) { // initialize timer1 for PWM output // - you may use 8,9, or 10 bit PWM resolution for timer 1 timer0PWMInit(); timer1PWMInit(8); timer2PWMInit(); timer0PWMOn(); timer1PWMAOn(); timer1PWMBOn(); timer2PWMOn(); timer0PWMSet(0); timer1PWMASet(0); timer1PWMBSet(0); timer2PWMSet(0); //timer1PWMOff(); } void inita2d(void) { // configure a2d port (PORTA) as input // so we can receive analog signals DDRA = 0x00; // make sure pull-up resistors are turned off PORTA = 0x00; // set the a2d prescaler (clock division ratio) // - a lower prescale setting will make the a2d converter go faster // - a higher setting will make it go slower but the measurements // will be more accurate // - other allowed prescale values can be found in a2d.h a2dSetPrescaler(ADC_PRESCALE_DIV32); // set the a2d reference // -the reference is the voltage against which a2d measurements are made // -other allowed reference values can be found in a2d.h a2dSetReference(ADC_REFERENCE_AVCC); // use a2dConvert8bit(channel#) to get an 8bit a2d reading // use a2dConvert10bit(channel#) to get a 10bit a2d reading } //----- Begin Code ------------------------------------------------------------ void mainloop(void) { u08 c; // print welcome message rprintfProgStrM("\r\nGadgetBoard 1.0 - Atmega32 (type help)\r\n"); // initialize cmdline system cmdlineInit(); // direct cmdline output to uart (serial port) cmdlineSetOutputFunc(uartSendByte); // add commands to the command database cmdlineAddCommand("a2d", reada2d); cmdlineAddCommand("pwm", setpwm); cmdlineAddCommand("out", setout); cmdlineAddCommand("setall", setall); cmdlineAddCommand("lights", lights); cmdlineAddCommand("echo",echo); cmdlineAddCommand("help",help); cmdlineAddCommand("?",help); // send a CR to cmdline input to stimulate a prompt cmdlineInputFunc('\r'); while(1) { // pass characters received on the uart (serial port) // into the cmdline processor while(uartReceiveByte(&c)) cmdlineInputFunc(c); // run the cmdline execution functions cmdlineMainLoop(); } } void echo(void) { cmdlineGetArgInt(1)?setecho(1):setecho(0); } void reada2d(void) { int pin=cmdlineGetArgInt(1); rprintf("%d\r\n", reada2d_(pin)); } int reada2d_(int pin) { if(pin < 1 || pin > 8) { rprintf("Invlid pin.\r\n"); return -1; } return a2dConvert8bit(pin-1); } void setall(void) { setall_(cmdlineGetArgStr(1)); } void setall_(char *val) { if(strlen(val) != 8) { rprintf("val must have exactly 8 characters.\r\n"); return; } int i; for(i=0; i<8; i++) { if((val[i] != '0') && (val[i] != '1')) { rprintf("val must be composed only of 1s and 0s.\r\n"); return; } } for(i=0; i<8; i++) { setout_(i+1, val[i]=='0' ? 0:1); } } void setout(void) { int out=cmdlineGetArgInt(1); int val=cmdlineGetArgInt(2); setout_(out,val); } void setout_(int out, int val) { if(out > 8 || out < 1) { rprintf("Invalid output.\r\n"); return; } if(val > 1 || val < 0) { rprintf("State must be 0 or 1.\r\n"); return; } switch(out) { case 1: timer1PWMASet(val?255:0); break; case 2: timer2PWMSet(val?255:0); break; case 3: if(val==0) { PORTC &= ~(1 << 6); } else { PORTC |= (1 << 6); } break; case 4: if(val==0) { PORTC &= ~(1 << 7); } else { PORTC |= (1 << 7); } break; case 5: if(val==0) { PORTC &= ~(1 << 0); } else { PORTC |= (1 << 0); } break; case 6: if(val==0) { PORTC &= ~(1 << 1); } else { PORTC |= (1 << 1); } break; case 7: timer0PWMSet(val?255:0); break; case 8: timer1PWMBSet(val?255:0); break; } } void setpwm(void) { int chan=cmdlineGetArgInt(1); int dc=cmdlineGetArgInt(2); setpwm_(chan,dc); } void setpwm_(int chan, int dc) { if(dc > 255 || dc < 0) { rprintf("Invalid duty cycle.\r\n"); return; } switch(chan) { case 7: timer0PWMSet(dc); break; case 1: timer1PWMASet(dc); break; case 8: timer1PWMBSet(dc); break; case 2: timer2PWMSet(dc); break; default: rprintf("Invalid channel.\r\n"); break; } } void help(void) { rprintfCRLF(); rprintf("Available commands are:\r\n"); rprintf("help - show available commands\r\n"); rprintf("a n - (ADC) show analog reading on input n\r\n"); rprintf("p n dc - (PWM) set duty cycle 'dc' on output 'n'\r\n"); rprintf(" n can be: 7(pwm0) 1(pwm1a) 8(pwm1b) 2(pwm2)\r\n"); rprintf(" dc can be: 0-255\r\n"); rprintf("o n val - Set output n (1-8) to val (0/1)\r\n"); rprintf("s val - Set all outputs according to val (eg. 11001001)\r\n"); rprintf("l - Enter stoplight mode\r\n"); rprintf("echo b - echo commands back (1) or not (0)\r\n"); rprintfCRLF(); } int main(void) { // initialize our libraries // initialize the UART (serial port) uartInit(); // For some reason, baud rate multipliers seem to be off // make all rprintf statements use uart for output rprintfInit(uartSendByte); // initialize the timer system timerInit(); // turn on and initialize A/D converter a2dInit(); inita2d(); // our own init routines initpwm(); // our own init routines initports(); mainloop(); return 0; }