Skip to content
Snippets Groups Projects
Commit 799e458d authored by Sergi Hernandez's avatar Sergi Hernandez
Browse files

Updated the firmware for the AX12 servos.

Solved a problem in the communications.
Implemented the EEPROM interface.
parent 351393ba
No related branches found
No related tags found
No related merge requests found
......@@ -2,7 +2,7 @@ PROJECT=ax12_fw
########################################################
# afegir tots els fitxers que s'han de compilar aquí
########################################################
SOURCES=src/main.c src/TXRX_Dynamixel.c src/CTRL_Dynamixel.c src/MEM_Dynamixel.c src/CFG_HW_Dynamixel.c
SOURCES=src/main.c src/gpio.c src/dyn_slave.c src/mem.c
OBJS=$(SOURCES:.c=.o)
SRC_DIR=./src/
INCLUDE_DIR=./include/
......
#ifndef DYN_SLAVE_H
#define DYN_SLAVE_H
// Instruction identifiers
#define INST_PING 0x01
#define INST_READ 0x02
#define INST_WRITE 0x03
#define INST_REG_WRITE 0x04
#define INST_ACTION 0x05
#define INST_RESET 0x06
#define INST_DIGITAL_RESET 0x07
#define INST_SYSTEM_READ 0x0C
#define INST_SYSTEM_WRITE 0x0D
#define INST_SYNC_WRITE 0x83
#define INST_SYNC_REG_WRITE 0x84
// bus errors
#define INSTRUCTION_ERROR 0x40
#define OVERLOAD_ERROR 0x20
#define CHECKSUM_ERROR 0x10
#define RANGE_ERROR 0x08
#define OVERHEATING_ERROR 0x04
#define ANGLE_LIMIT_ERROR 0x02
#define VOLTAGE_ERROR 0x01
#define NO_ERROR 0x00
void dyn_slave_init(uint8_t baudrate,uint8_t id);
void dyn_slave_set_baudrate(uint8_t baudrate);
void dyn_slave_set_id(uint8_t id);
uint8_t dyn_slave_is_packet_ready(void);
uint8_t dyn_slave_check_id(void);
uint8_t dyn_slave_check_checksum(void);
uint8_t dyn_slave_get_instruction(void);
uint8_t dyn_slave_get_address(void);
uint8_t dyn_slave_get_read_length(void);
uint8_t dyn_slave_get_write_length(void);
uint8_t *dyn_slave_get_write_data(void);
void dyn_slave_send_status(uint8_t error,uint8_t length, uint8_t *data);
uint8_t dyn_slave_is_send_done(void);
#endif
#ifndef GPIO_H
#define GPIO_H
void gpio_init(void);
void gpio_led_on(void);
void gpio_led_off(void);
#endif
#ifndef _MEM_H
#define _MEM_H
#include <avr/io.h>
// Register Id EEPROM - declared in dynamixel
#define Model_Number_L 0X00 //0 - 0X0C R - Lowest byte of model number
#define Model_Number_H 0X01 //1 - 0X00 R - Highest byte of model number
#define Version_Firmware 0X02 //2 - 0X00 R - Information on the version of firmware
#define ID 0X03 //3 - 0X03 R/W - ID of Dynamixel
#define Baud_Rate 0X04 //4 - 0X01 R/W - Baud Rate of Dynamixel
#define Return_Delay_Time 0X05 //5 - 0XFA R/W - Return Delay Time
#define CW_Angle_Limit_L 0X06 //6 - 0X00 R/W - Lowest byte of clockwise Angle Limit
#define CW_Angle_Limit_H 0X07 //7 - 0X00 R/W - Highest byte of clockwise Angle Limit
#define CCW_Angle_Limit_L 0X08 //8 - 0XFF R/W - Lowest byte of counterclockwise Angle Limit
#define CCW_Angle_Limit_H 0X09 //9 - 0X03 R/W - Highest byte of counterclockwise Angle Limit
// 10 no used
#define Int_Limit_Temperature 0X0B //11 - 0X46 R/W - Internal Highest Limit Temperature
#define Low_Limit_Voltage 0X0C //12 - 0X3C R/W - the Lowest Limit Voltage
#define High_Limit_Voltage 0X0D //13 - 0XBE R/W - the Highest Limit Voltage
#define Max_Torque_L 0X0E //14 - 0XFF R/W - Lowest byte of Max. Torque
#define Max_Torque_H 0X0F //15 - 0X03 R/W - Highest byte of Max. Torque
#define Status_Return_Level 0X10 //16 - 0X02 R/W - Status Return Level
#define Alarm_LED 0X11 //17 - 0x24 R/W - LED for Alarm
#define Alarm_Shutdown 0X12 //18 - 0x24 R/W - Shutdown for Alarm
// Register Id RAM - declared in dynamixel
#define Torque_Enable 0X18 //24 - 0x00 R/W - Torque On/Off
#define LED 0X19 //25 - 0x00 R/W - LED On/Off
#define CW_Compliance_Margin 0X1A //26 - 0X01 R/W - CW Compliance margin
#define CCW_Compliance_Margin 0X1B //27 - 0X01 R/W - CCW Compliance margin
#define CW_Compliance_Slope 0X1C //28 - 0X20 R/W - CW Compliance slope
#define CCW_Compliance_Slope 0X1D //29 - 0X20 R/W - CCW Compliance Slope
#define Goal_Position_L 0X1E //30 - 0x00 R/W - Lowest byte of Goal Position
#define Goal_Position_H 0X1F //31 - 0x00 R/W - Highest byte of Goal Position
#define Moving_Speed_L 0X20 //32 - 0xF0 R/W - Lowest byte of Moving Speed
#define Moving_Speed_H 0X21 //33 - 0x0F R/W - Highest byte of Moving Speed
#define Torque_Limit_L 0X22 //34 - 0xF0 R/W - Lowest byte of Torque Limit
#define Torque_Limit_H 0X23 //35 - 0x0F R/W - Highest byte of Torque Limit
#define Present_Position_L 0X24 //36 - 0x00 R - Lowest byte of Current Position Position_Value
#define Present_Position_H 0X25 //37 - 0x00 R - Highest byte of Current Position
#define Present_Speed_L 0X26 //38 - 0x00 R - Lowest byte of Current Speed
#define Present_Speed_H 0X27 //39 - 0x00 R - Highest byte of Current Speed
#define Present_Load_L 0X28 //40 - 0x00 R - Lowest byte of Current Load
#define Present_Load_H 0X29 //41 - 0x00 R - Highest byte of Current Load
#define Present_Voltage 0X2A //42 - 0x00 R - Current Voltage
#define Present_Temperature 0X2B //43 - 0x00 R - Current Temperature
#define Registered 0X2C //44 - 0x00 R - Means if Instruction is registered
//45 no used
#define Moving 0X2E //46 - 0X00 R - Means if there is any movement
#define Lock 0X2F //47 - 0x00 R/W - Locking EEPROM
#define Punch_L 0X30 //48 - 0X20 R/W - Lowest byte of Punch
#define Punch_H 0X31 //49 - 0X00 R/W - Highest byte of Punch
void ram_init(void);
uint8_t ram_read(uint8_t address, uint8_t length, uint8_t **data);
uint8_t ram_write(uint8_t address, uint8_t length, uint8_t *data);
#endif
#include <avr/interrupt.h>
#include <avr/io.h>
#include "dyn_slave.h"
#include "gpio.h"
uint8_t dyn_slave_id;
uint8_t dyn_slave_data[128];
uint8_t dyn_slave_num_bytes;
uint8_t dyn_slave_packet_ready;
uint8_t dyn_slave_send_done;
inline void dyn_slave_set_rx(void)
{
PORTD &= 0x7F;
PORTD |= 0x40;
}
inline void dyn_slave_set_tx(void)
{
PORTD &= 0xBF;
PORTD |= 0x80;
}
ISR(USART_TXC_vect)
{
UDR=dyn_slave_data[dyn_slave_num_bytes];
if(dyn_slave_num_bytes==dyn_slave_data[3]+4)
{
dyn_slave_num_bytes=0;
dyn_slave_set_rx();
dyn_slave_send_done=1;
UCSRB&=~(1<<TXCIE);// disable tx interrutps
UCSRA|=0x80;// clear any pending interrupt
UCSRB|=(1<<RXCIE);// enable reception interrupts
}
else
dyn_slave_num_bytes++;
}
ISR(USART_RXC_vect)
{
static uint8_t length;
cli();// disable any other interrupt
dyn_slave_data[dyn_slave_num_bytes]=UDR;
switch(dyn_slave_num_bytes)
{
case 0: if(dyn_slave_data[dyn_slave_num_bytes]==0xFF)
dyn_slave_num_bytes++;
break;
case 1: if(dyn_slave_data[dyn_slave_num_bytes]==0xFF)
dyn_slave_num_bytes++;
else
dyn_slave_num_bytes--;
break;
case 2: dyn_slave_num_bytes++;
break;
case 3: length=dyn_slave_data[dyn_slave_num_bytes]+3;
dyn_slave_num_bytes++;
break;
default: if(dyn_slave_num_bytes==length)
{
dyn_slave_num_bytes=0;
dyn_slave_packet_ready=1;
UCSRB&=~(1<<RXCIE);// disable the rx interrupt
}
else
dyn_slave_num_bytes++;
break;
}
sei();// enable all interrutps
}
void dyn_slave_init(uint8_t baudrate,uint8_t id)
{
// DDRD - Port D Data Direction Register
DDRD=DDRD|0xC2;// RX_en, TX_en, TX are outputs 0xC2=11000010
DDRD=DDRD&0xFE;// RX is an input 0xFE=11111110
// configure the ports to receive data
dyn_slave_set_rx();
// initialize the rs485 ports
UCSRA = (1<<U2X);// double USART transmission speed
dyn_slave_set_baudrate(baudrate);
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<< UCSZ0); // 8 bit data, no parity, 1 stop bit
dyn_slave_set_id(id);
// initialize private variables
dyn_slave_num_bytes=0;
dyn_slave_packet_ready=0;
dyn_slave_send_done=0;
}
void dyn_slave_set_baudrate(uint8_t baudrate)
{
UBRRH = 0;
UBRRL = baudrate;
}
void dyn_slave_set_id(uint8_t id)
{
dyn_slave_id=id;
}
uint8_t dyn_slave_is_packet_ready(void)
{
if(dyn_slave_packet_ready==0x01)
{
dyn_slave_packet_ready=0x00;
return 0x01;
}
else
return 0x00;
}
uint8_t dyn_slave_check_id(void)
{
if(dyn_slave_id==dyn_slave_data[2])
return 0x01;
else
return 0x00;
}
uint8_t dyn_slave_check_checksum(void)
{
uint8_t i,cs=0x00;
for(i=2;i<dyn_slave_data[3]+4;i++)
cs+=dyn_slave_data[i];
if(cs==0xFF)
return 0x01;
else
return 0x00;
}
uint8_t dyn_slave_get_instruction(void)
{
return dyn_slave_data[4];
}
uint8_t dyn_slave_get_address(void)
{
return dyn_slave_data[5];
}
uint8_t dyn_slave_get_read_length(void)
{
return dyn_slave_data[6];
}
uint8_t dyn_slave_get_write_length(void)
{
return dyn_slave_data[3]-3;
}
uint8_t *dyn_slave_get_write_data(void)
{
return &dyn_slave_data[6];
}
void dyn_slave_send_status(uint8_t error,uint8_t length, uint8_t *data)
{
uint8_t i,cs=0;
dyn_slave_data[0]=0xFF;
dyn_slave_data[1]=0xFF;
dyn_slave_data[2]=dyn_slave_id;
cs+=dyn_slave_id;
dyn_slave_data[3]=length+2;
cs+=length;
dyn_slave_data[4]=error;
cs+=error;
for(i=0;i<length;i++)
{
dyn_slave_data[5+i]=data[i];
cs+=data[i];
}
dyn_slave_data[5+i]=~cs;
// set in tex mode
dyn_slave_set_tx();
// start transmission
UCSRA|=0x40;// clear any pending interrupt
UCSRB|=(1<<TXCIE);
dyn_slave_num_bytes=1;
UDR=dyn_slave_data[0];
}
uint8_t dyn_slave_is_send_done(void)
{
return dyn_slave_send_done;
}
#include "gpio.h"
#include <avr/io.h>
#define LED_PIN PD2
void gpio_init(void)
{
DDRD |= (1 << LED_PIN); // Set LED as output
gpio_led_off();
}
void gpio_led_on(void)
{
PORTD &= ~(1<<LED_PIN); // ON LED
}
void gpio_led_off(void)
{
PORTD |= (1<<LED_PIN); // off LED
}
......@@ -4,275 +4,66 @@
// be aware -- NO MESSAGGE WILL OCCUR BUT LOSS OF INFO WILL OCCOUR
#include <avr/io.h>
//#include <util/delay.h>
#include <avr/interrupt.h>
#include "TXRX_Dynamixel.h"
#include "CTRL_Dynamixel.h"
#include "MEM_Dynamixel.h"
#include "CFG_HW_Dynamixel.h"
#include <avr/eeprom.h>
#define NULL (void *)0x00000000
#define FALSE 0
#define TRUE 1
//*************CTRL = INTERRUPT FUNCTION *********************************
uint16_t count = 0;
uint16_t count2 = 0;
unsigned char do_control;
ISR( TIMER0_OVF_vect) {
int16_t TorqueEnable;
TCNT0 = 0x00;
sei();
TorqueEnable = Read_byte_Dynamixel_RAM(Torque_Enable);
if (TorqueEnable == 1) {
//if (1){
//cli(); // disable all interrupts just to make sure
//TIMSK &= ~( 1 << TOIE0); // disable timer0
count++;
if (count == 2) {
do_control = 1;
//cli(); // disable all interrupts just to make sure
// 1.3s passed
// LedTONGGLE();
//Control_Cycle();
//HW_Security();
//Write_Actuator();
count = 0;
}
//TIMSK |= ( 1 << TOIE0); // enable timer0
//sei(); // enable all interrupts
} else {
OCR1A = CTRL_ZERO; //PB1 => set PWM for X% duty cycle
OCR1B = CTRL_ZERO; //PB2 => set PWM for Y% duty cycle
}
#include <util/delay.h>
#include <avr/interrupt.h>
#include "gpio.h"
#include "dyn_slave.h"
#include "mem.h"
void do_write(uint8_t address,uint8_t length,uint8_t *data)
{
uint8_t i,num=0;
// check is EEPROM space
for(i=address;i<address+length;i++)
{
if(i==ID)
dyn_slave_set_id(data[num]);
num++;
}
}
//**************** M A I N *********************************
int16_t main(void) {
unsigned char data[128], id, length, instruction, answer[2], status,
en_vector, i;
// list of read only registers - to exclude from write
unsigned char read_only_vector[30] = { 0, 1, 2, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 67, 68, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 83,
84, 85, 86 };
// ini eeprom if first time after run reflash system
if (eeprom_read_byte((uint8_t*) Gate_Restore_Eeprom) != 0xCC) {
Restore_Eeprom_Factory_Values(); //once this procedure is held, no more initialization is performed
}
// to force write to eeprom un-comment
//Restore_Eeprom_Factory_Values(); //once this procedure is held, no more initialization is performed
//------EEPROM initial values-------------
Restore_EepromVAR();
// get value of Motor ID
rs485_address = eepromVAR[ID];
// configure AVR chip i/o
Config_Hardware();
// Assign actual position to goal position and assume zero motor turns
Ini_Position();
// initialize the RS485 interface
init_RS485();
// end of initialization
LedOFF;
do_control = 0;
while (1) {
status = RxRS485Packet(&id, &instruction, &length, data);
if (status == CHECK_ERROR) {
TxRS485Packet(rs485_address, CHECKSUM_ERROR, 0, NULL);
} else if (status == CORRECT) {
if (id == rs485_address) { //1) {
switch (instruction) {
//***********************************************************
// P I N G
case INST_PING:
TxRS485Packet(rs485_address, NO_ERROR, 0, NULL);
break;
//*********************************************************
// R E A D and send info to pc
case INST_READ:
if (data[0] <= 18) { // if 0 <= IDinstruction <= 18 caso eeprom
if (data[0] >= 0) {
switch (data[1]) // tamaño del paquete que pide
{
case 1: // case BYTE
TxRx_Read_byte_Dynamixel_EEPROM(data[0],
answer);
break; //................................................
case 2: // case WORD
TxRx_Read_word_Dynamixel_EEPROM(data[0],
answer);
break; //................................................
default:
TxRS485Packet(rs485_address, INSTRUCTION_ERROR,
0, NULL);
break; //................................................
}
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0,
NULL);
} // else id error
} else if (data[0] <= 86) { // else if 24 <= ID <= 86 ram
if (data[0] >= 24) {
switch (data[1]) { // tamaño del paquete que pide
case 1: // case BYTE
TxRx_Read_byte_Dynamixel_RAM(data[0], answer);
break; //................................................
case 2: // case WORD
//LedTOGGLE;
TxRx_Read_word_Dynamixel_RAM(data[0], answer);
break; //................................................
default:
TxRS485Packet(rs485_address, INSTRUCTION_ERROR,
0, NULL);
break; //................................................
}
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0,
NULL);
} // else id error
} else if (data[0] == 87) { // else if ID = 87 ram
switch (data[1]) { // tamaño del paquete que pide
case 1: // case BYTE
TxRx_Read_byte_Dynamixel_RAM(data[0], answer);
break; //................................................
default:
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0,
NULL);
break; //................................................
}
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0,
NULL);
} // else id error
break;
//**********************************************************
// W R I T E info send by pc
case INST_WRITE:
en_vector = FALSE;
for (i = 0; i < 30; i++) {
if (data[0] == read_only_vector[i]) {
en_vector = TRUE;
break;
}
}
if (en_vector == FALSE) {
if (data[0] <= 18) { // if 0 <= IDinstruction <= 18 caso eeprom
if (data[0] >= 0) {
if (Read_byte_Dynamixel_RAM(Lock) == 1) { // is eeprom write lock open
switch (length) // tamaño del paquete que pide
{
case 2: // case BYTE
TxRx_Write_byte_Dynamixel_EEPROM(
data[0], data, length);
break; //................................................
case 3: // case WORD
TxRx_Write_word_Dynamixel_EEPROM(
data[0], data, length);
break; //................................................
default:
TxRS485Packet(rs485_address,
INSTRUCTION_ERROR, 0, NULL);
break; //................................................
}
switch (data[0]) // aditional instructions for particular registers in eeprom
{
case ID: // case EEMPROM and BYTE
rs485_address = eepromVAR[ID];
break; //..................................................
case Baud_Rate: // case EEMPROM and BYTE
Baud_Rate_Value();
break; //..................................................
default:
;
break; //................................................
}
Write_byte_Dynamixel_RAM(Lock, 0); // close eeprom write lock
} else {
TxRS485Packet(rs485_address,
INSTRUCTION_ERROR, 0, NULL);
} // eeprom write lock closed
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR,
0, NULL);
} // else id error
} else if (data[0] <= 86) { // else if 24 <= ID <= 86 ram
if (data[0] >= 24) {
switch (length) { // tamaño del paquete que pide
case 2: // case BYTE
TxRx_Write_byte_Dynamixel_RAM(data[0], data,
length);
break; //................................................
case 3: // case WORD
TxRx_Write_word_Dynamixel_RAM(data[0], data,
length);
break; //................................................
default:
TxRS485Packet(rs485_address,
INSTRUCTION_ERROR, 0, NULL);
break; //................................................
}
switch (data[0]) { // aditional instructions for particular registers in ram
case LED: // case RAM and BYTE
if (Read_byte_Dynamixel_RAM(LED) == 1) {
LedON;
} else {
LedOFF;
}
break; //................................................
default:
;
break; //................................................
}
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR,
0, NULL);
} // else id error
} else if (data[0] == 87) { // else if ID = 87 ram // case BYTE
TxRx_Write_byte_Dynamixel_RAM(data[0], data,
length);
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0,
NULL);
} // else id error
} else {
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0,
NULL);
} // else in only read register list
break;
//**********************************************************
//********* D E F A U L T *****************************
default:
TxRS485Packet(rs485_address, INSTRUCTION_ERROR, 0, NULL);
break;
//***************************************************
}
}
} else if (do_control) {
Control_Cycle();
do_control = 0;
//sei(); // enable all interrupts
}
}
int16_t main(void)
{
uint8_t *data;
gpio_init();
dyn_slave_init(34,1);
ram_init();
sei();
while (1)
{
if(dyn_slave_is_packet_ready())
{
if(dyn_slave_check_id())
{
if(dyn_slave_check_checksum())
{
switch(dyn_slave_get_instruction())
{
case INST_PING: dyn_slave_send_status(NO_ERROR,0x00,0x00000000);
break;
case INST_READ: if(ram_read(dyn_slave_get_address(),dyn_slave_get_read_length(),&data))
dyn_slave_send_status(NO_ERROR,dyn_slave_get_read_length(),data);
else
dyn_slave_send_status(INSTRUCTION_ERROR,0x00,0x00000000);
break;
case INST_WRITE: if(ram_write(dyn_slave_get_address(),dyn_slave_get_write_length(),dyn_slave_get_write_data()))
{
do_write(dyn_slave_get_address(),dyn_slave_get_write_length(),dyn_slave_get_write_data());
dyn_slave_send_status(NO_ERROR,0x00,0x00000000);
}
else
dyn_slave_send_status(INSTRUCTION_ERROR,0x00,0x00000000);
break;
default: dyn_slave_send_status(INSTRUCTION_ERROR,0x00,0x00000000);
break;
}
}
else
dyn_slave_send_status(CHECKSUM_ERROR,0x00,0x00000000);
}
}
}
}
#include "mem.h"
#include <avr/eeprom.h>
// dynamixel RAM variables
unsigned char ram_data[50];
// Dynamixel EEPROM variables
unsigned char EEMEM eeprom_data[19]={0x0C,0x00,0x00,0x01,0x22,0xFA,0x00,0x00,0xFF,0x03,0x00,0x50,0x3C,0xF0,0xFF,0x03,0x02,0x24,0x24};
void ram_init(void)
{
uint8_t i;
for(i=0;i<Alarm_Shutdown;i++)
ram_data[i]=eeprom_read_byte(&eeprom_data[i]);
for(;i<Punch_H;i++)
ram_data[i]=0x00;
ram_data[CW_Compliance_Slope]=0x20;
ram_data[CCW_Compliance_Slope]=0x20;
ram_data[Torque_Limit_L]=ram_data[Max_Torque_L];
ram_data[Torque_Limit_H]=ram_data[Max_Torque_H];
ram_data[Punch_L]=0x20;
}
uint8_t ram_read(uint8_t address, uint8_t length, uint8_t **data)
{
if((address+length)>Punch_H)
return 0x00;
else
{
(*data)=&ram_data[address];
return 0x01;
}
}
uint8_t ram_write(uint8_t address, uint8_t length, uint8_t *data)
{
uint8_t i,num=0;
if((address+length)>Punch_H)
return 0x00;
else
{
for(i=address;i<address+length;i++)
{
if(i<=Alarm_Shutdown)
eeprom_write_byte(&eeprom_data[i],data[num]);
ram_data[i]=data[num];
num++;
}
return 0x01;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment