This is late alpha code for the new H-bridge controller board. This is incomplete and very crufty, but what is here works.
// AB_AVR_PWM_03.cpp // Ed Bennett 10-21-09 /* This is a .cpp file which compiles against the arduino libraries without the .pde extension. The makefile is hacked from the arduino playground makefile for use with your own code editor. The makefile builds and burns the program with the avrispmkii by typing "make". The makefile can also burn the fuses by typing "make fuses". Do this on a new chip. The .cpp program file is like a normal MCU C program except that init() must be called to start up the wiring code. (1) make an empty folder with the same name as your .cpp file will have, like "my_mcu_prog_v1" (2) copy the makefile into the folder and change it so that TARGET = my_mcu_prog_v1 SAVE THE FILE (3) in the new folder, create, copy, or open your .cpp program file like "my_mcu_prog_v1.cpp" in an editor like textedit or Smultron (4) open a terminal window in the my_mcu_prog_v1 folder (5) edit your code (6) type make in the terminal to build 'n burn (7) goto (5) Note: avrdude coughs up an error at the end of the burn: "Verify error - unable to read lfuse properly. Programmer may not be reliable." The error seems harmless. Its origin is unknown. */ /* ATMEGA168 DIP-28 PIN MAPPINGS TO ARDUINO DIECIMIELIA & AB H-BRIDGE CONTROLLER DIP-28 AVR Func Ardu AB Mask 1 C6 RST RST RST 2 D0 RXD D0/RX AB_TO_MCU 3 D1 TXD D1/TX MCU_TO_AB 4 D2 D2 D2 D4 PD 1<<2 5 D3 D3 D3/PWM PWM1/D5 PD 1<<3 6 D4 D4 D4 -- 7 V+ -- -- -- 8 DGND -- -- -- 9 XTAL -- -- -- 10 XTAL -- -- -- 11 D5 OC0B D5/PWM D1 PD 1<<5 12 D6 OC0A D6/PWM -- 13 D7 D7 D7 D3 PD 1<<7 14 B0 B0 D8 D2 PB 1<<0 15 B1 OC1A D9/PWM PWM2/D6 PB 1<<1 16 B2 OC1B D10/PWM PWM3/D7 PB 1<<2 17 B3 OC2A D11/PWM PWM4/D8 PB 1<<3 18 B4 B4 D12 -- 19 B5 B5 D13 -- 20 AVCC -- -- -- 21 AREF -- -- -- 22 GND -- -- -- 23 C0 ADC0 A0 -- 24 C1 ADC1 A1 ADDR_VALID LED PC 1<<1 25 C2 ADC2 A2 -- 26 C3 ADC3 A3 SETUP SWITCH PC 1<<3 27 C4 ADC4 A4 ERROR LED PC 1<<4 28 C5 ADC5 A5 RS585 TX=H,RX=L PC 1<<5 */ #include "WProgram.h" #include <avr/io.h> #include <avr/wdt.h> #include <ctype.h> // from a post by Eric Weddington on avrfreaks.net // p is a byte variable, m is the bit position in a mask /* #define DIO_1 PORTD,5 #define DIO_2 PORTB,0 #define DIO_3 PORTD,7 #define DIO_4 PORTD,2 #define DIO_5 PORTD,3 #define DIO_6 PORTB,1 #define DIO_7 PORTB,2 #define DIO_8 PORTB,3 */ // i/o bit Mask for AB ports B and D #define DM1 0x20 #define DM2 0x01 #define DM3 0x80 #define DM4 0x04 #define DM5 0x08 #define DM6 0x02 #define DM7 0x04 #define DM8 0x08 // port associated with a pin #define DP1 PORTD #define DP2 PORTB #define DP3 PORTD #define DP4 PORTD #define DP5 PORTD #define DP6 PORTB #define DP7 PORTB #define DP8 PORTB // DDR asscoicated with a pin #define DR1 DDRD #define DR2 DDRB #define DR3 DDRD #define DR4 DDRD #define DR5 DDRD #define DR6 DDRB #define DR7 DDRB #define DR8 DDRB /* # define PORTD_MASK (0x1D) // portd pins we use # define PORTB_MASK (0xE2) # define PORTD_MASK (0xB8) // portd pins we use # define PORTB_MASK (0x47) */ # define PORTD_MASK (0xAC) // portd pins we use # define PORTB_MASK (0x0F) #define ADDR_LED 1 // my address is valid LED - pin C1 #define ERROR_LED 4 // there was an error LED - pin C4 #define RXTX_SWITCH 5 // line controls data direction on the // RS-485 transciever chip - pin C5 XMIT=H, RCV=L // errors - values add. A get OVERFLOW, 1 and a UNDERFLOW, 2 // together would cause error_last to have the value 3 #define OVERFLOW 0b00000001 // get overflow flag (1) #define UNDERFLOW 0b00000010 // get underflow flag (2) #define TIMEOUT_FLG 0b00000100 // ser in timeout flag (4) #define BAD_CMD 0b00001000 // invalid command (8) #define BAD_VAL 0b00010000 // bad param value (16) #define FAST_COM 1 // 115200 baud #define SLOW_COM 0 // 19200 baud // top level states #define WAITING 0 #define CHK_ADDR 1 #define RECV_CMD 2 #define RECV_PARM 3 #define RUNNING_CMD 4 #define PAUSED 5 #define SPACE 0x20 #define BEGIN_XMIT PORTC |= 1 << RXTX_SWITCH; delayMicroseconds(200); // 100uS // minimum -- doubled for safety #define END_XMIT PORTC &= ~(1 << RXTX_SWITCH); void reboot(void); int MyBug_1_pwm(void); int clearPin(int pin); int setPin(int pin); int getInt(int *converted); char myAddr = 'A'; // default values char myGroup = 'Z'; byte state; byte errByte; byte ledPin = 8; // LED connected to digital pin 14 //int pwmPin = 9; // Promiscous listen to bus during idle. Wait for "Attention" character (!). byte waitForAttention(void){ while(Serial.read() != '!'); return 1; } int clearPin(int pin){ switch(pin){ case 1: DDRD |= (1 << 5); PORTD &= ~(1 << 5); break; case 2: DDRB |= (1 << 0); PORTB &= ~(1 << 0); break; case 3: DDRD |= (1 << 7 ); PORTD &= ~(1 << 7); break; case 4: DDRD |= (1 << 2); PORTD &= ~(1 << 2); break; case 5: DDRD |= (1 << 3); PORTD &= ~(1 << 3); break; case 6: DDRB |= (1 << 1); PORTB &= ~(1 << 1); break; case 7: DDRB |= (1 << 2); PORTB &= ~(1 << 2); break; case 8: DDRB |= (1 << 3); PORTB &= ~(1 << 3); break; default: errByte |= BAD_VAL; BEGIN_XMIT Serial.print("bad pin"); END_XMIT return 0; //fail break; } return 1; //succeed } int setPin(int pin){ switch(pin){ case 1: DDRD |= (1 << 5); PORTD |= (1 << 5); break; case 2: DDRB |= (1 << 0); PORTB |= (1 << 0); break; case 3: DDRD |= (1 << 7); PORTD |= (1 << 7); break; case 4: DDRD |= (1 << 2); PORTD |= (1 << 2); break; case 5: DDRD |= (1 << 3); PORTD |= (1 << 3); break; case 6: DDRB |= (1 << 1); PORTB |= (1 << 1); break; case 7: DDRB |= (1 << 2); PORTB |= (1 << 2); break; case 8: DDRB |= (1 << 3); PORTB |= (1 << 3); break; default: errByte |= BAD_VAL; BEGIN_XMIT Serial.print("bad pin"); END_XMIT return 0; //fail break; } return 1; //succeed } int putByte(int outByte){ int mask = 1; DDRD = PORTD_MASK; // set all to output DDRB = PORTB_MASK; if(mask & outByte){ // pin 1 PORTD |= (1 << 5); } else { PORTD &= ~(1 << 5); } mask <<= 1; if(mask & outByte){ // pin 2 PORTB |= (1 << 0); } else { PORTB &= ~(1 << 0); } mask <<= 1; if(mask & outByte){ // pin 3 PORTD |= (1 << 7); } else { PORTD &= ~(1 << 7); } mask <<= 1; if(mask & outByte){ // pin 4 PORTD |= (1 << 2); } else { PORTD &= ~(1 << 2); } mask <<= 1; if(mask & outByte){ // pin 5 PORTD |= (1 << 3); } else { PORTD &= ~(1 << 3); } mask <<= 1; if(mask & outByte){ // pin 6 PORTB |= (1 << 1); } else { PORTB &= ~(1 << 1); } mask <<= 1; if(mask & outByte){ // pin 7 PORTB |= (1 << 2); } else { PORTB &= ~(1 << 2); } mask <<= 1; DDRB |= (1 << 3); if(mask & outByte){ // pin 8 PORTB |= (1 << 3); } else { PORTB &= ~(DM8); } return 1; //succeed } int readPin(int pin){ byte read_val = 0; switch(pin){ case 1: DDRD &= ~(1 << 5); // set pin to input read_val = PORTD & (1 << 5); break; case 2: DDRB &= ~(1 << 0); read_val = PORTB & (1 << 0); break; case 3: DDRD &= ~(1 << 7); read_val = PORTD & (1 << 7); break; case 4: DDRD &= ~(1 << 2); read_val = PORTD & (1 << 2); break; case 5: DDRD &= ~(1 << 3); read_val = PORTD & (1 << 3); break; case 6: DDRB &= ~(1 << 1); read_val = PORTB & (1 << 1); break; case 7: DDRB &= ~(1 << 2); read_val = PORTB & (1 << 2); break; case 8: DDRB &= ~(1 << 3); read_val = PORTB & (1 << 3); break; default: errByte |= BAD_VAL; BEGIN_XMIT Serial.print("bad pin"); END_XMIT return 0; //fail break; } return read_val; // succeed } /* switch(which_line){ case 1: read_val = input(IN_OUT_1); break; case 2: read_val = input(IN_OUT_2); break; case 3: read_val = input(IN_OUT_3); break; case 4: read_val = input(IN_OUT_4); break; case 5: read_val = input(IN_OUT_5); break; case 6: read_val = input(IN_OUT_6); break; case 7: read_val = input(IN_OUT_7); break; case 8: read_val = input(IN_OUT_8); break; } // let bogus values fall through without comment start_print(); printf("%u\n",read_val); end_print(); return 1; }else{ return 0; } } */ int process_cmd(int cmd){ int which_line = 0; int portVal; switch(cmd){ /* case 'a': getInt(&which_line); // which line to write? readPin(which_line); break; case 'b': MyBug_read_byte(); break; case 'i': MyBug_toggle_pin(); break; */ case 'j': getInt(&which_line); // which line to write? clearPin(which_line); break; case 'k': getInt(&which_line); // which line to write? setPin(which_line); break; case 'n': getInt(&portVal); putByte(portVal); break; case 'p': MyBug_1_pwm(); return WAITING; break; case 'q': //x break; case 'g': //x break; case 'h': return PAUSED; break; case 'w': //x break; case 'x': BEGIN_XMIT Serial.println("foo"); END_XMIT break; case 'y': //x break; case 'z': reboot(); case ';': // sometimes these slip through break; default: errByte &= BAD_CMD; BEGIN_XMIT Serial.print("bad cmd in switch"); Serial.println(cmd); END_XMIT break; } } /* This is an Atmel hack for rebooting. It also requires this to be at the very top of main(): MCUSR = 0; wdt_disable(); */ void reboot(void){ WDTCSR = _BV(WDE); wdt_reset(); wdt_enable(WDTO_30MS); while (1); // DIE! } int MyBug_1_pwm(){ int pwmChan; int pwmVal; int AB_chan; if(!(getInt(&AB_chan))){ /* BEGIN_XMIT Serial.println("Bad get pwmChan MyBug"); END_XMIT */ return 0; } // convert ArtBus PWM pin names to arduino pin names switch (AB_chan){ case 1: pwmChan = 3; // AVR pin # 5 break; case 2: pwmChan = 9; // AVR pin # 15 break; case 3: pwmChan = 10; // AVR pin # 16 break; case 4: pwmChan = 11; // AVR pin # 17 break; default: //error = badparm; /* BEGIN_XMIT Serial.print("unknown chan "); Serial.println(AB_chan); END_XMIT */ break; } if(!(getInt(&pwmVal))){ /* BEGIN_XMIT Serial.println("Bad get pwmVal 2 @ MyBug"); END_XMIT */ return 0; } pinMode(pwmChan, OUTPUT); analogWrite(pwmChan, pwmVal); } // when we are expecting data, a timeout means an error byte getExpectedData(){ char val; long time; long timeout; timeout=time=millis(); // start counting milliseconds // try getting serial data for 1 to 2 milliseconds (it will vary) while(time - timeout < 2){ if(Serial.available()){ val = Serial.read(); return val; } time=millis(); } /* BEGIN_XMIT Serial.println("serial timeout @ getExpected"); END_XMIT */ return 0; // while loop expired before data arrived (error) } int getChar(char *ascVal) { // returns success char asciival; // scratchpad variable asciival = getExpectedData(); if (isascii(asciival)){ // null would be bogus here *ascVal = asciival; /* BEGIN_XMIT Serial.print("asciival @ getChar "); Serial.println(asciival); END_XMIT */ return 1; } /* BEGIN_XMIT Serial.println("no char @ getChar"); END_XMIT */ return 0; // failed from timeout } int getInt(int *converted) { // returns success char asciival; char stringarray[6]; int i = 0; int tries = 0; // fill the character array for conversion to an int do { asciival = getExpectedData(); if (asciival){ // null would be bogus here if ((asciival == '\n') || (asciival == '\r')) continue; stringarray[i] = asciival; i++; }else{ /* BEGIN_XMIT Serial.println("do-while @ getInt()"); END_XMIT */ return 0; // failed from bad char } }while((isdigit(asciival)) && (i<=5)); // 5-char string + \0 -- do-while /* BEGIN_XMIT Serial.print("asciival @414 .."); Serial.print(asciival); Serial.println(".."); END_XMIT */ if (asciival == ';' || asciival == ' ') { // delimiter is last member of array // add end of array marker for stringiness stringarray[i] = 0; //overwrite delimiter with null to make string *converted = atoi(stringarray); //didn't check for legit ascii return 1; }else{ /* BEGIN_XMIT Serial.println("bad convert @ getInt()"); END_XMIT */ return 0; } } void blinkADDR_LED() { PORTC |= 1 << ADDR_LED; delay(250); PORTC &= ~(1 << ADDR_LED); delay(250); } void greet(void){ char n; n = (myAddr - 'A') * 3; // ~30 millis per letter of the alphabet delay(n*10); // so each unit prints at a different time blinkADDR_LED(); blinkADDR_LED(); blinkADDR_LED(); BEGIN_XMIT Serial.print("\n\rHi from "); Serial.print(myAddr); Serial.println("! I'm an ArtBus H-Bridge controller. (rev 0.091020) \n\r"); END_XMIT } // ----------------------------------------------------------------------------- int main(void){ char val; /* After a software reset, "!Az;" the watchdog timer remains running and must be disabled to keep out of an endless reset loop. The MCU Status Register is cleared first (it likes it that way) so that the watchdog timer can then be disabled. The WDT is only used here as a way to reboot the chip. */ MCUSR = 0; wdt_disable(); init(); Serial.begin(115200); DDRC = 0xF7; // port c all out except setup switch greet(); state = WAITING; while(1){ switch(state){ case WAITING: waitForAttention(); // ENTIRE PROGRAM BLOCKS HERE val = getExpectedData(); if(val){ state = CHK_ADDR; }else{ state = WAITING; } break; case CHK_ADDR: if(myAddr == val){ state = RUNNING_CMD; PORTC |= 1 << ADDR_LED; // hi. }else{ state = WAITING; PORTC &= ~(1<<aDDR_LED); } break; case RUNNING_CMD: if(getChar(&val)){ state = process_cmd(val); // returns WAITING }else{ state = WAITING; } break; default: state = WAITING; } } }