-
Sergi Hernandez authored
Changed the way the 16 bit variables are accessed in the incomming and outgoing packets. STM32L0 family generated a system fault with the previous approach.
Sergi Hernandez authoredChanged the way the 16 bit variables are accessed in the incomming and outgoing packets. STM32L0 family generated a system fault with the previous approach.
dynamixel_slave.c 19.67 KiB
#include "dynamixel_slave.h"
/* private functions */
unsigned char dyn_slave_irq_receive_cb(void *dyn_slave,unsigned char byte)
{
TDynamixelSlave *dyn=(TDynamixelSlave *)dyn_slave;
if(dyn->comm_dev->time!=0x00000000)
time_set_timeout(dyn->comm_dev->time,dyn->rx_timeout_ms*1000);
switch(dyn->received_bytes)
{
case 0: if(byte==0xFF)
{
dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
}
break;
case 1: if(byte==0xFF)
{
dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
}
else
dyn->received_bytes--;
break;
case 2: if(byte==0xFD)// version 2 header
{
if(dyn->version==DYN_VER2)// the module is configured for version 2
{
dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
}
else
dyn->received_bytes=0;// ignore packet and restart synchronization
}
else if(byte!=0xFF)
{
dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
}
break;
case 3: dyn->rx_buffer[dyn->received_bytes]=byte;
if(dyn->version==DYN_VER1)
{
dyn->op_length=byte;
dyn->received_bytes=0;
/* finish reception by IRQ */
comm_cancel_irq_receive(dyn->comm_dev);
/* enable dma RX */
comm_receive_dma(dyn->comm_dev,&dyn->rx_buffer[4],dyn->op_length);
}
else
dyn->received_bytes++;
break;
case 4: dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
break;
case 5: dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
dyn->op_length=byte;
break;
case 6: dyn->rx_buffer[dyn->received_bytes]=byte;
dyn->received_bytes++;
dyn->op_length+=(byte<<8);
dyn->received_bytes=0;
/* finish reception by IRQ */
comm_cancel_irq_receive(dyn->comm_dev);
/* enable dma RX */
comm_receive_dma(dyn->comm_dev,&dyn->rx_buffer[7],dyn->op_length);
break;
default: break;
}
return 0x00;
}
unsigned char dyn_slave_dma_send_cb(void *dyn_slave)
{
TDynamixelSlave *dyn=(TDynamixelSlave *)dyn_slave;
// enable tx interrupts
dyn->set_rx_mode();
return 0x00;
}
unsigned char dyn_slave_dma_receive_cb(void *dyn_slave)
{
TDynamixelSlave *dyn=(TDynamixelSlave *)dyn_slave;
comm_receive_irq(dyn->comm_dev,0);// reenable reception by IRQ
if(dyn->comm_dev->time!=0x00000000)
time_cancel_timeout(dyn->comm_dev->time);
dyn->packet_ready=0x01;
return 0x00;
}
void dummy_dyn_slave_set_tx_mode(void)
{
}
void dummy_dyn_slave_set_rx_mode(void)
{
}
void dummy_dyn_slave_on_ping(void)
{
}
unsigned char dummy_dyn_slave_on_read(unsigned short int address,unsigned short int length,unsigned char *data)
{
return DYN_SUCCESS;
}
unsigned char dummy_dyn_slave_on_write(unsigned short int address,unsigned short int length,unsigned char *data)
{
return DYN_SUCCESS;
}
unsigned char dummy_dyn_slave_on_reset(void)
{
return DYN_SUCCESS;
}
unsigned char dummy_dyn_slave_on_relay(TDynVersion version,unsigned char *inst_pkt,unsigned char *status_pkt)
{
return DYN_NO_DEVICE;
}
void dyn_slave_send_status_packet(TDynamixelSlave *slave,unsigned char error,unsigned short int length, unsigned char *data)
{
// wait until the previous transmission has ended (if any)
while(comm_is_send_done(slave->comm_dev)==COMM_BUSY);
if(slave->return_delay>0)
if(slave->comm_dev->time!=0x00000000)
time_delay_us(slave->comm_dev->time,slave->return_delay<<1);
if(slave->version==DYN_VER1)
{
// create the status packet
dyn_init_status_packet(slave->tx_buffer,slave->address,error,length,data);
// set the tx mode, if necessary
slave->set_tx_mode();
// start transmission by DMA
comm_send_dma(slave->comm_dev,slave->tx_buffer,dyn_get_length(slave->tx_buffer)+4);
}
else
{
// create the status packet
dyn2_init_status_packet(slave->tx_buffer,slave->address,error,length,data);
// set the tx mode, if necessary
slave->set_tx_mode();
// start transmission by DMA
comm_send_dma(slave->comm_dev,slave->tx_buffer,dyn2_get_length(slave->tx_buffer)+7);
}
}
void dyn_v1_slave_loop(TDynamixelSlave *slave)
{
static unsigned char data[MAX_DYN_SLAVE_TX_BUFFER_LEN],error,length,address,prev_id,id;
id=dyn_get_id(slave->rx_buffer);
if(id==slave->address || id==DYN_BROADCAST_ID)// the packet is addressed to this device or it is a broadcast
{
// check the packet checksum
if(dyn_check_checksum(slave->rx_buffer)==0xFF)// the incomming packet is okay
{
// process the packet
switch(dyn_get_instruction(slave->rx_buffer))
{
case DYN_PING: slave->on_ping();
if(id!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,0,data);
break;
case DYN_READ: error=slave->on_read(dyn_get_read_address(slave->rx_buffer),dyn_get_read_length(slave->rx_buffer),data);
if(slave->return_level!=no_return && id!=DYN_BROADCAST_ID)
{
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,dyn_get_read_length(slave->rx_buffer),data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
break;
case DYN_WRITE: length=dyn_get_write_data(slave->rx_buffer,data);
error=slave->on_write(dyn_get_write_address(slave->rx_buffer),length,data);
if(slave->return_level==return_all && id!=DYN_BROADCAST_ID)
{
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,0,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
break;
case DYN_REG_WRITE: slave->reg_length=dyn_get_reg_write_data(slave->rx_buffer,slave->reg_buffer);
slave->reg_address=dyn_get_reg_write_address(slave->rx_buffer);
if(slave->return_level==return_all && id!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,0,data);
break;
case DYN_ACTION: if(slave->reg_address!=0xFFFF)
{
error=slave->on_write(slave->reg_address,slave->reg_length,slave->reg_buffer);
slave->reg_address=0xFFFF;
}
else
if(slave->return_level==return_all && id!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
break;
case DYN_RESET:
break;
case DYN_SYNC_READ: dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
break;
case DYN_SYNC_WRITE: if(dyn_sync_write_id_present(slave->rx_buffer,slave->address,&address,&length,data))// the device is addressed
error=slave->on_write(address,length,data);
break;
case DYN_BULK_READ: prev_id=dyn_bulk_read_id_present(slave->rx_buffer,slave->address,&address,&length);
if(prev_id!=0xFF)
{
if(prev_id==0x00)// first device to answer
{
error=slave->on_read(address,length,data);
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,length,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
else// wait for the previous device in the sequence to send its data
{
slave->sync_bulk_address=address;
slave->sync_bulk_length=length;
slave->sync_bulk_prev_id=prev_id;
slave->bulk_read_pending=0x01;
}
}
break;
case DYN_BULK_WRITE: dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
break;
default:
break;
}
}
else
{
// send a checksum error answer
if(dyn_get_id(slave->rx_buffer)!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_CHECKSUM_ERROR,0,0x00);
}
}
else
{
if(slave->bulk_read_pending)
{
if(id==slave->sync_bulk_prev_id)
{
slave->bulk_read_pending=0x00;
error=slave->on_read(slave->sync_bulk_address,slave->sync_bulk_length,data);
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,slave->sync_bulk_length,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
}
else// the packet is addressed to another device, so relay it
{
if(slave->on_relay(slave->version,slave->rx_buffer,slave->tx_buffer)==DYN_SUCCESS)
{
// set the tx mode, if necessary
slave->set_tx_mode();
// start transmission by DMA
comm_send_dma(slave->comm_dev,slave->tx_buffer,dyn_get_length(slave->tx_buffer)+4);
}
}
}
}
void dyn_v2_slave_loop(TDynamixelSlave *slave)
{
static unsigned char data[MAX_DYN_SLAVE_TX_BUFFER_LEN],error,prev_id,id;
static unsigned short int length,address;
id=dyn2_get_id(slave->rx_buffer);
if(id==slave->address || id==DYN_BROADCAST_ID)// the packet is addressed to this device or it is a broadcast
{
// check the packet checksum
if(dyn2_check_checksum(slave->rx_buffer)==0x01)// the incomming packet is okay
{
// process the packet
switch(dyn2_get_instruction(slave->rx_buffer))
{
case DYN_PING: slave->on_ping();
if(id!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,0,data);
break;
case DYN_READ: error=slave->on_read(dyn2_get_read_address(slave->rx_buffer),dyn2_get_read_length(slave->rx_buffer),data);
if(slave->return_level!=no_return && id!=DYN_BROADCAST_ID)
{
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,dyn2_get_read_length(slave->rx_buffer),data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
break;
case DYN_WRITE: length=dyn2_get_write_data(slave->rx_buffer,data);
error=slave->on_write(dyn2_get_write_address(slave->rx_buffer),length,data);
if(slave->return_level==return_all && id!=DYN_BROADCAST_ID)
{
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,0,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
break;
case DYN_REG_WRITE: slave->reg_length=dyn2_get_reg_write_data(slave->rx_buffer,slave->reg_buffer);
slave->reg_address=dyn2_get_reg_write_address(slave->rx_buffer);
if(slave->return_level==return_all && id!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,0,data);
break;
case DYN_ACTION: if(slave->reg_address!=0xFFFF)
{
error=slave->on_write(slave->reg_address,slave->reg_length,slave->reg_buffer);
slave->reg_address=0xFFFF;
}
else
if(slave->return_level==return_all && id!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
break;
case DYN_RESET:
break;
case DYN_SYNC_READ: prev_id=dyn2_sync_read_id_present(slave->rx_buffer,slave->address,&address,&length);
if(prev_id!=0xFF)
{
if(prev_id==0x00)// first device to answer
{
error=slave->on_read(address,length,data);
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,length,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
else// wait for the previous device in the sequence to send its data
{
slave->sync_bulk_address=address;
slave->sync_bulk_length=length;
slave->sync_bulk_prev_id=prev_id;
slave->sync_read_pending=0x01;
}
}
break;
case DYN_SYNC_WRITE: if(dyn2_sync_write_id_present(slave->rx_buffer,slave->address,&address,&length,data))// the device is addressed
error=slave->on_write(address,length,data);
break;
case DYN_BULK_READ: prev_id=dyn2_bulk_read_id_present(slave->rx_buffer,slave->address,&address,&length);
if(prev_id!=0xFF)
{
if(prev_id==0x00)// first device to answer
{
error=slave->on_read(address,length,data);
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,length,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
else// wait for the previous device in the sequence to send its data
{
slave->sync_bulk_address=address;
slave->sync_bulk_length=length;
slave->sync_bulk_prev_id=prev_id;
slave->bulk_read_pending=0x01;
}
}
break;
case DYN_BULK_WRITE: if(dyn2_bulk_write_id_present(slave->rx_buffer,slave->address,&address,&length,data))
error=slave->on_write(address,length,data);
break;
default:
break;
}
}
else
{
// send a checksum error answer
if(dyn_get_id(slave->rx_buffer)!=DYN_BROADCAST_ID)
dyn_slave_send_status_packet(slave,DYN_CHECKSUM_ERROR,0,0x00);
}
}
else
{
if(slave->bulk_read_pending)
{
if(id==slave->sync_bulk_prev_id)
{
slave->bulk_read_pending=0x00;
error=slave->on_read(slave->sync_bulk_address,slave->sync_bulk_length,data);
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,slave->sync_bulk_length,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
}
else if(slave->sync_read_pending)
{
if(id==slave->sync_bulk_prev_id)
{
slave->sync_read_pending=0x00;
error=slave->on_read(slave->sync_bulk_address,slave->sync_bulk_length,data);
if(error==DYN_NO_ERROR)
dyn_slave_send_status_packet(slave,DYN_NO_ERROR,slave->sync_bulk_length,data);
else
dyn_slave_send_status_packet(slave,DYN_INST_ERROR,0,data);
}
}
else// the packet is addressed to another device, so relay it
{
if(slave->on_relay(slave->version,slave->rx_buffer,slave->tx_buffer)==DYN_SUCCESS)
{
// set the tx mode, if necessary
slave->set_tx_mode();
// start transmission by DMA
comm_send_dma(slave->comm_dev,slave->tx_buffer,dyn2_get_length(slave->tx_buffer)+8);
}
}
}
}
/* public functions */
void dyn_slave_init(TDynamixelSlave *slave,TComm *dev,unsigned char address,TDynVersion version)
{
/* assign communication functions */
dev->irq_receive_cb=dyn_slave_irq_receive_cb;
dev->dma_send_cb=dyn_slave_dma_send_cb;
dev->dma_receive_cb=dyn_slave_dma_receive_cb;
slave->comm_dev=dev;
slave->version=version;
dev->data=slave;
/* initialize the internal callbacks */
slave->set_tx_mode=dummy_dyn_slave_set_tx_mode;
slave->set_rx_mode=dummy_dyn_slave_set_rx_mode;
slave->on_ping=dummy_dyn_slave_on_ping;
slave->on_read=dummy_dyn_slave_on_read;
slave->on_write=dummy_dyn_slave_on_write;
slave->on_reset=dummy_dyn_slave_on_reset;
slave->on_relay=dummy_dyn_slave_on_relay;
/* initialize internal variables */
slave->address=address;
slave->return_delay=0x00;
slave->return_level=return_all;
slave->packet_ready=0x00;
slave->rx_timeout_ms=50;
slave->received_bytes=0x00;
slave->reg_address=0xFFFF;
slave->reg_length=0x0000;
slave->sync_bulk_address=0x0000;
slave->sync_bulk_length=0x0000;
slave->sync_bulk_prev_id=0x00;
slave->sync_read_pending=0x00;
slave->bulk_read_pending=0x00;
slave->set_rx_mode();
/* enable reception by IRQ */
comm_receive_irq(slave->comm_dev,0);
}
void dyn_slave_set_rx_timeout(TDynamixelSlave *slave,unsigned short int timeout_ms)
{
slave->rx_timeout_ms=timeout_ms;
}
inline void dyn_slave_set_address(TDynamixelSlave *slave,unsigned char address)
{
slave->address=address;
}
inline unsigned char dyn_slave_get_address(TDynamixelSlave *slave)
{
return slave->address;
}
inline void dyn_slave_set_return_delay(TDynamixelSlave *slave,unsigned char delay)
{
slave->return_delay=delay;
}
inline unsigned char dyn_slave_get_return_delay(TDynamixelSlave *slave)
{
return slave->return_delay;
}
inline void dyn_slave_set_return_level(TDynamixelSlave *slave,return_level_t level)
{
slave->return_level=level;
}
inline return_level_t dyn_slave_get_return_level(TDynamixelSlave *slave)
{
return slave->return_level;
}
inline TDynVersion dyn_slave_get_version(TDynamixelSlave *slave)
{
return slave->version;
}
void dyn_slave_loop(TDynamixelSlave *slave)
{
if(slave->packet_ready)// check if a new instruction packet has been received
{
slave->packet_ready=0x00;
// check address
if(slave->version==DYN_VER1)
dyn_v1_slave_loop(slave);
else
dyn_v2_slave_loop(slave);
}
else
{
if(slave->comm_dev->time!=0x00000000)
{
if(time_is_timeout(slave->comm_dev->time))
{
/* cancel any IRQ or DMA reception */
comm_cancel_irq_receive(slave->comm_dev);
comm_cancel_dma_receive(slave->comm_dev);
slave->received_bytes=0;
/* enable reception by IRQ */
comm_receive_irq(slave->comm_dev,0);
}
}
}
}