Driving the H-bridge controller from the AVR
ian – Tue, 2006 – 07 – 04 12:15
The following code will test that the AVR and H-bridge driver are communicating properly. It assumes that there's an H-bridge and motor attached to PWM channel 0. It will run the motor at full power in one direction, then reverse the direction every few seconds. As I soon found out, this is a pretty tough on the hardware. This is intended for the ATMega32. I'm using WinAVR for compilation.
#include < avr/io.h>
#include < avr/interrupt.h>
#include < util/twi.h>
typedef unsigned char uchar;
void delay_ms(unsigned char ms)
/* badly mangled delay_ms */
{
unsigned short outer1, outer2;
outer1 = 1;
while (outer1) {
outer2 = 1000;
while (outer2) {
while ( ms ) ms--;
outer2--;
}
outer1--;
}
}
void wait_a_bit(void)
{
int i;
for (i = 0; i < 150; i++)
delay_ms(0);
}
void led_mid(void)
{
OCR1A = 80;
}
void led_off(void)
{
OCR1A = 0;
}
void led_on(void)
{
OCR1A = 255;
}
void twi_init(void)
{
// set TWI clock rate
TWSR |= 3; // div by 64
TWBR = 4; // div by another 3
// final clock rate should be about 10kHz
/*set initial state of TWCR
FIXME stub*/
}
void debug(uchar val)
{
PORTA = val;
}
uchar twi_write_byte(uchar addr, uchar data)
{
// I-bit in SREG must be set
// write to TWCR to start transmission: TWINT 1, TWSTA 1, TWSTO 0, TWEN 1
TWCR = (1 << TWINT | 1 << TWSTA | 1 << TWEN);
while (!(TWCR & (1 << TWINT)))
;
if (((TWSR & 0xf8) != TW_START) && ((TWSR & 0xf8) != TW_REP_START)) // added by Ian; repeated START is OK
return 1; // error
TWDR = (addr << 1) | TW_WRITE;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
;
if ((TWSR & 0xf8) != TW_MT_SLA_ACK)
return 2; // error
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
;
if ((TWSR & 0xf8) != TW_MT_DATA_ACK)
return 3; // error
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while (!(TWCR & (1 << TWSTO))) // Ian's addition: make sure STOP condition has finished
;
return 0;
}
void debug_init(void)
{
DDRA = 0xff;
}
void wait_fast(void)
{
int i;
for (i = 0; i < 30; i++)
delay_ms(0);
}
int main(void)
{
int err = 0;
/* enable PD5 as output */
DDRD |= 1 << PD5;
/* set up the PWM */
TCCR1A |= 1 << COM1A1; // clear OC1A/OC1B on compare match during upcount; set on match during downcount
TCCR1A |= 1 << WGM10; // PWM, phase correct, 8-bit
TCCR1B |= 1 << CS10; // internal clock, 1:1 prescale
/* we want to set the LED to mid brightness during init, then set it to full-on if i2c transmit is successful, full-off otherwise */
debug_init();
led_on();
debug(0xff);
wait_a_bit();
led_mid();
debug(0x0);
wait_a_bit();
twi_init();
/*
; The truth table for the output is:
; Reverse Brake | A B C D
; --------------+--------
; 0 0 | P 0 0 P
; 0 1 | 0 0 P P
; 1 0 | 0 P P 0
; 1 1 | 0 0 P P
twi_command:
; command format: 8 bits
; ccdbssss
; cc = channel number. Only the LSB is used on this device.
; d = direction. 0 forward, 1 backwards
; b = brake. 0 no brake, 1 brake
; ssss = speed. higher values -> higher speed
*/
int speed = 0;
int count = 0;
int dir = 0;
while (1) {
dir = ~dir;
//if (dir)
// speed++;
speed = 0xf;
int addr = 0b1000000;
int data_base = 0b00000000;
int data = data_base | (speed & 0xf) | (dir & 0b00100000);
debug(1);
err = twi_write_byte(addr, data);
debug(data);
/* want to send the command '01001010' to address '1100000' as a write */
//err = twi_write_byte(0b1000000, 0b01111010); // 2/3 brake port 1
//wait_a_bit();
//err = twi_write_byte(0b1000000, 0b00001000); // 1/2 fwd port 0
//err = twi_write_byte(0b1000000, 0b01111010); // 2/3 brake port 1
//err = twi_write_byte(0b1000000, 0b00101010); // 2/3 back port 0
//err = twi_write_byte(0b1000000, 0b01101010); // 2/3 back port 1
//err = twi_write_byte(0b1000000, 0b01001010); // 2/3 forward port 1
if (err) {
while (1) {
led_off();
debug(err);
wait_a_bit();
led_on();
debug(TWSR);
wait_a_bit();
}
} else {
led_on();
}
//debug(speed);
wait_a_bit();
wait_a_bit();
}
return 0; // we should never hit this
}
