Alpha code for AVR

 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;
            }
    
        }
}