好久没更新技术博客了,前段时间看了MODBUS,写了一份主从机通信框架分享给大家
#include "comm.h"
#define COM_BUF_SIZE 30
#define COM_ADDR_BASE 0xA0 //slave address
#define COM_FRAME_SIZE_MIN (6-2*COM_SOH_EOT)
#define COM_FRAME_ADDR_OFF (COM_SOH_EOT+0)
#define COM_FRAME_FUNC_OFF (COM_SOH_EOT+1)
#define COM_FRAME_LEN_OFF (COM_SOH_EOT+2)
#define COM_FRAME_DATA_OFF (COM_SOH_EOT+3)
#define COM_FRAME_START 0xFE
#define COM_FRAME_END 0xFF
#define COM_FUNC_ERROR 0x80
#define COM_FUNC_RESP 0x40
typedef enum
{
STATE_RX_IDLE = 0,
STATE_RX_RCV
} COM_RECV_STATE;
typedef enum
{
STATE_TX_IDLE = 0,
STATE_TX_XMIT
} COM_SEND_STATE;
typedef struct
{
COM_SEND_STATE send_state;
COM_RECV_STATE recv_state;
U8 com_recv_buf[COM_BUF_SIZE];
U8 com_send_buf[COM_BUF_SIZE];
U8 recv_buf_pos;
U8 recv_buf_len;
U8 send_buf_pos;
U8 send_buf_len;
}_COMM_DATA;
#if (COM_MASTER_MODE == 1)
volatile U8 master_user_send_data[2][COM_USER_BUF_SIZE];
volatile U8 master_user_recv_data[2][COM_USER_BUF_SIZE];
#else
volatile U8 slave_user_send_data[COM_USER_BUF_SIZE];
volatile U8 slave_user_recv_data[COM_USER_BUF_SIZE];
#endif
static EVENT_TYPE event = EV_NONE, curr_event = EV_NONE;
static U8 event_in_queue = FALSE;
static U32 com_tout_cnt = 0, req_tout_cnt = 0, req_handing = 1;
static volatile U8 slave_address = 0xA0;
static volatile _COMM_DATA comm_data;
static void rx_tx_enable(U8 rx_en, U8 tx_en);
static void uart_get_byte(U8 *c);
static void uart_put_byte(U8 c);
static void uart_init(U32 baud);
static void com_tout35_irq(void);
static COM_ERRNO slave_broadcast(U8 *recv_buf, U8 recv_len, U8 **send_buf, U8 *send_len);
static COM_ERRNO slave_func_01(U8 *recv_buf, U8 recv_len, U8 **send_buf, U8 *send_len);
static COM_ERRNO master_broadcast(U8 saddr, U8 *recv_buf, U8 recv_len);
static COM_ERRNO master_func_01(U8 saddr, U8 *recv_buf, U8 recv_len);
__interrupt void SCIATx_ISR(void);
__interrupt void SCIARx_ISR(void);
#if (COM_MASTER_MODE == 1)
#define MB_FUNC_HANDLERS_MAX 1
static FUNC_MASTER xfunc_master[MB_FUNC_HANDLERS_MAX] =
{
{0x01, master_func_01}
};
#else
#define MB_FUNC_HANDLERS_MAX 1
static FUNC_SLAVE xfunc_slave[MB_FUNC_HANDLERS_MAX] =
{
{0x01, slave_func_01}
};
#endif
U16 com_checksum(U8 data[], U16 len)
{
U16 sum = 0, i;
for(i = 0; i < len; i++)
{
sum += data[i];
}
sum = ~sum;
sum &= 0xFF;
return sum;
}
void * mem_move(void * dest, const void *src, U16 count)
{
U8 *tmp, *s;
if (src == dest)
return dest;
if (dest <= src)
{
tmp = (U8 *) dest;
s = (U8 *) src;
while (count--)
*tmp++ = *s++;
}
else
{
tmp = (U8 *) dest + count;
s = (U8 *) src + count;
while (count--)
*--tmp = *--s;
}
return dest;
}
void event_init(void)
{
event = EV_NONE;
}
U8 post_event(EVENT_TYPE ev)
{
event = ev;
return TRUE;
}
U8 get_event(EVENT_TYPE *ev)
{
if(event != EV_NONE)
{
*ev = event;
curr_event = event;
event = EV_NONE;
return TRUE;
}
return FALSE;
}
U8 get_curr_event(EVENT_TYPE *ev)
{
*ev = curr_event;
return TRUE;
}
COM_ERRNO com_frame_recv(U8 *recv_addr, U8 *func_code, U8 **recv_data, U8 *recv_len)
{
COM_ERRNO status = E_NONE;
if((comm_data.recv_buf_len >= COM_FRAME_SIZE_MIN))
//&& com_checksum(comm_data.com_recv_buf, comm_data.recv_buf_len) == ((0xFF+COM_SOH_EOT*COM_FRAME_END)&0xFF))
{
*recv_addr = comm_data.com_recv_buf[COM_FRAME_ADDR_OFF];
*func_code = comm_data.com_recv_buf[COM_FRAME_FUNC_OFF];
*recv_len = comm_data.com_recv_buf[COM_FRAME_LEN_OFF];
*recv_data = (U8 *) &comm_data.com_recv_buf[COM_FRAME_DATA_OFF];
}
else
{
status = E_BADVAL;
}
return status;
}
COM_ERRNO com_frame_send(U8 send_addr, U8 func_code, const U8* send_data, U8 send_len)
{
COM_ERRNO status = E_NONE;
U8 i, chksum = 0;
if(send_len == 0)
{
rx_tx_enable(TRUE, FALSE);
return status;
}
if(comm_data.recv_state == STATE_RX_IDLE)
{
comm_data.send_buf_len = 0;
#if (COM_SOH_EOT == 1)
comm_data.com_send_buf[comm_data.send_buf_len++] = COM_FRAME_START;
#else
#endif
comm_data.com_send_buf[comm_data.send_buf_len++] = send_addr;
comm_data.com_send_buf[comm_data.send_buf_len++] = func_code;
comm_data.com_send_buf[comm_data.send_buf_len++] = send_len;
for(i = 0; i < send_len; i++)
{
comm_data.com_send_buf[comm_data.send_buf_len++] = send_data[i];
}
chksum = com_checksum(comm_data.com_send_buf, comm_data.send_buf_len);
comm_data.com_send_buf[comm_data.send_buf_len++] = chksum;
#if (COM_SOH_EOT == 1)
comm_data.com_send_buf[comm_data.send_buf_len++] = (U8)COM_FRAME_END;
#else
#endif
comm_data.send_buf_pos = 0;
comm_data.send_state = STATE_TX_XMIT;
rx_tx_enable(FALSE, TRUE);
uart_put_byte(comm_data.com_send_buf[comm_data.send_buf_pos++]);
com_tout_cnt = 1;
}
else
{
status = E_BADVAL;
}
return status;
}
void com_recv_irq(void)
{
U8 byte = 0xCC, i, cnt;
if(comm_data.send_state != STATE_TX_IDLE) return;
uart_get_byte(&byte);
//dbg_recv_data[dbg_recv_point++] = byte;
if(comm_data.recv_state == STATE_RX_IDLE)
{
comm_data.recv_buf_len = 0;
#if (COM_SOH_EOT == 1)
if(byte == COM_FRAME_START)
#else
if((byte & COM_ADDR_BASE) == COM_ADDR_BASE)
#endif
{
comm_data.com_recv_buf[comm_data.recv_buf_len++] = byte;
comm_data.recv_state = STATE_RX_RCV;
}
com_tout_cnt = 1;
}
else if(comm_data.recv_state == STATE_RX_RCV)
{
com_tout_cnt = 1;
comm_data.com_recv_buf[comm_data.recv_buf_len++] = byte;
if(comm_data.recv_buf_len < COM_BUF_SIZE)
{
#if (COM_SOH_EOT == 1)
if(comm_data.recv_buf_len < COM_FRAME_SIZE_MIN)
{
switch(comm_data.recv_buf_len-1)
{
case COM_FRAME_ADDR_OFF:
if((byte & COM_ADDR_BASE) != COM_ADDR_BASE)
{
comm_data.recv_state = STATE_RX_IDLE;
}
break;
case COM_FRAME_FUNC_OFF:
break;
case COM_FRAME_LEN_OFF:
if(byte > COM_BUF_SIZE)
{
comm_data.recv_state = STATE_RX_IDLE;
}
break;
default:
break;
}
}
else if(comm_data.com_recv_buf[COM_FRAME_LEN_OFF]+6 == comm_data.recv_buf_len)
{
if(byte == COM_FRAME_END)
{
rx_tx_enable(FALSE, FALSE);
//post_event(EV_FRAME_RECV);
#if (COM_MASTER_MODE == 1)
post_event(EV_FRAME_RECV);
#else
post_event(EV_FRAME_RECV);
#endif
comm_data.recv_state = STATE_RX_IDLE;
com_tout_cnt = 0;
}
else
{
for(i = 1, cnt = comm_data.recv_buf_len; i < cnt; i++)
{
if(comm_data.com_recv_buf[i] == COM_FRAME_START)
{
mem_move((U8 *)&comm_data.com_recv_buf[0], (U8 *)&comm_data.com_recv_buf[i], comm_data.recv_buf_len-i);
comm_data.recv_buf_pos = i;
comm_data.recv_buf_len -= i;
break;
}
}
if(i >= cnt)
{
comm_data.recv_state = STATE_RX_IDLE;
}
}
}
#else
#endif
}
else
{
comm_data.recv_state = STATE_RX_IDLE;
}
}
else
{
comm_data.recv_state = STATE_RX_IDLE;
}
}
void com_send_irq(void)
{
if(comm_data.recv_state != STATE_RX_IDLE) return;
if(comm_data.send_state == STATE_TX_IDLE)
{
rx_tx_enable(TRUE, FALSE);
}
else if(comm_data.send_state == STATE_TX_XMIT)
{
com_tout_cnt = 1;
if(comm_data.send_buf_pos < comm_data.send_buf_len)
{
uart_put_byte(comm_data.com_send_buf[comm_data.send_buf_pos++]);
}
else
{
#if (COM_MASTER_MODE == 1)
#else
post_event(EV_FRAME_SENT);
#endif
com_tout_cnt = 0;
comm_data.send_state = STATE_TX_IDLE;
rx_tx_enable(TRUE, FALSE);
}
}
}
void com_tout35_irq(void)
{
com_tout_cnt = 0;
if(comm_data.recv_state != STATE_RX_IDLE)
{
#if (COM_SOH_EOT == 1)
#else
rx_tx_enable(FALSE, FALSE);
post_event(EV_FRAME_RECV);
#endif
comm_data.recv_state = STATE_RX_IDLE;
}
if(comm_data.send_state != STATE_TX_IDLE)
{
//post_event(EV_FRAME_RECV);
comm_data.send_state = STATE_TX_IDLE;
rx_tx_enable(TRUE, FALSE);
}
}
void timer_expired_1ms(void)
{
if(com_tout_cnt && com_tout_cnt < 0xFFFF)
{
com_tout_cnt++;
}
if(com_tout_cnt > 3)
{
com_tout35_irq();
com_tout_cnt = 0;
}
#if (COM_MASTER_MODE == 1)
if(req_tout_cnt)
{
req_tout_cnt--;
}
#endif
}
COM_ERRNO com_init(U8 slave_addr, U32 baud_rate)
{
COM_ERRNO status = E_NONE;
U32 tout35_us = ~0;
slave_address = slave_addr;
uart_init(baud_rate);
if(baud_rate >= 19200)
{
tout35_us = 1823; /* 1823us. */
}
else
{
tout35_us = 35000000UL / baud_rate;
}
event_init();
com_tout_cnt = 0;
MemSet((U8 *)&comm_data, 0, sizeof(_COMM_DATA));
#if (COM_MASTER_MODE == 1)
MemSet(master_user_send_data, 0, sizeof(master_user_send_data));
MemSet(master_user_recv_data, 0, sizeof(master_user_recv_data));
rx_tx_enable(FALSE, TRUE);
#else
MemSet((U8 *)slave_user_send_data, 0, sizeof(slave_user_send_data));
MemSet((U8 *)slave_user_recv_data, 0, sizeof(slave_user_recv_data));
rx_tx_enable(TRUE, FALSE);
#endif
return status;
}
#if (COM_MASTER_MODE == 0)
COM_ERRNO com_slave_poll(void)
{
static U8 recv_addr, func_code = 0, recv_len = 0, send_len = 0, i;
static U8 *recv_data;
static U8 *send_data = NULL;
static COM_ERRNO except_code;
COM_ERRNO status = E_NONE;
EVENT_TYPE ev;
if(get_event(&ev) == TRUE)
{
switch(ev)
{
case EV_FRAME_RECV:
status = com_frame_recv(&recv_addr, &func_code, &recv_data, &recv_len);
if(status == E_NONE && (recv_addr == slave_address || recv_addr == COM_BOARDCAST_ADDR))
{
post_event(EV_EXEC_FUNC);
}
else
{
rx_tx_enable(TRUE, FALSE);
}
break;
case EV_EXEC_FUNC:
except_code = E_BADRQC;
if(recv_addr == COM_BOARDCAST_ADDR)
{
except_code = slave_broadcast(recv_data, recv_len, &send_data, &send_len);
}
else
{
for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++)
{
if(xfunc_slave[i].func_code == func_code)
{
except_code = xfunc_slave[i].pfun_callback(recv_data, recv_len, &send_data, &send_len);
break;
}
}
}
if(except_code)
{
*send_data = except_code;
send_len = 1;
status = com_frame_send(slave_address, func_code|COM_FUNC_ERROR, send_data, send_len);
}
else
{
status = com_frame_send(slave_address, func_code|COM_FUNC_RESP, send_data, send_len);
}
post_event(EV_FRAME_SENT);
break;
case EV_FRAME_SENT:
break;
}
}
return status;
}
//emergency situations such as shut down
COM_ERRNO slave_broadcast(U8 *recv_buf, U8 recv_len, U8 **send_buf, U8 *send_len)
{
*send_len = 0; // no response expect
return E_NONE;
}
COM_ERRNO slave_func_01(U8 *recv_buf, U8 recv_len, U8 **send_buf, U8 *send_len)
{
MemCpy((U8 *)slave_user_recv_data, recv_buf, recv_len);
*send_buf = (U8 *)slave_user_send_data;
*send_len = sizeof(slave_user_send_data);
return E_NONE;
}
#else
U8 get_request_result(COM_ERRNO *err)
{
EVENT_TYPE ev;
if(req_handing)
{
get_curr_event(&ev);
if(ev == EV_EXEC_DONE)
{
*err = E_NONE;
req_handing = 0;
return TRUE;
}
else if(req_tout_cnt == 0)
{
*err = E_TOUT;
req_handing = 0;
return TRUE;
}
else
{
return FALSE;
}
}
return FALSE;
}
COM_ERRNO master_request(U8 addr, U8 func, U8 *send_data, U8 send_len, U32 tout_ms)
{
static COM_ERRNO status = E_NONE;
req_tout_cnt = tout_ms;
req_handing = 1;
slave_address = addr;
comm_data.recv_state = STATE_RX_IDLE;
status = com_frame_send(addr, func, send_data, send_len);
if(addr == COM_BOARDCAST_ADDR)
{
post_event(EV_EXEC_DONE);
}
else
{
post_event(EV_FRAME_SENT);
}
status = E_HANDLING;
return status;
}
COM_ERRNO com_master_poll(void)
{
static U8 recv_addr, func_code = 0, recv_len = 0, send_len = 0, i;
static U8 *recv_data;
static COM_ERRNO except_code;
COM_ERRNO status = E_NONE;
EVENT_TYPE ev;
if(get_event(&ev) == TRUE)
{
switch(ev)
{
case EV_FRAME_RECV:
status = com_frame_recv(&recv_addr, &func_code, &recv_data, &recv_len);
if(status == E_NONE && (recv_addr == slave_address || recv_addr == COM_BOARDCAST_ADDR))
{
post_event(EV_EXEC_FUNC);
}
else
{
rx_tx_enable(TRUE, FALSE);
}
break;
case EV_EXEC_FUNC:
except_code = E_BADRQC;
if(recv_addr == COM_BOARDCAST_ADDR)
{
//except_code = master_broadcast(slave_address, recv_data, recv_len);
}
else
{
for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++)
{
if(xfunc_master[i].func_code == func_code)
{
except_code = xfunc_master[i].pfun_callback(slave_address, recv_data, recv_len);
break;
}
}
}
post_event(EV_EXEC_DONE);
break;
case EV_FRAME_SENT:
break;
case EV_EXEC_DONE:
rx_tx_enable(TRUE, FALSE);
break;
}
}
return status;
}
COM_ERRNO master_func_01(U8 saddr, U8 *recv_buf, U8 recv_len)
{
MemCpy(master_user_recv_data[saddr-COM_ADDR_BASE], recv_buf, recv_len);
return E_NONE;
}
COM_ERRNO master_broadcast(U8 saddr, U8 *recv_buf, U8 recv_len)
{
return E_NONE;
}
#endif
/******************porting*************************/
void rx_tx_enable(U8 rx_en, U8 tx_en)
{
//EALLOW;
if(rx_en)
{
SciaRegs.SCICTL2.bit.RXBKINTENA = 1;
}
else
{
SciaRegs.SCICTL2.bit.RXBKINTENA = 0;
}
if(tx_en)
{
SciaRegs.SCICTL2.bit.TXINTENA = 1;
}
else
{
SciaRegs.SCICTL2.bit.TXINTENA = 0;
}
NOP;
NOP;
NOP;
//SciaRegs.SCICTL1.all = 0x23;
}
void uart_init(U32 baud)
{
U32 brr_reg;
brr_reg = ((15000000UL*10)/(baud*8)+5)/10-1;
InitSciGpio();
}
void uart_put_byte(U8 c)
{
SciaRegs.SCITXBUF = c;
}
void uart_get_byte(U8 *c)
{
*c = SciaRegs.SCIRXBUF.all&0xFF;
}
__interrupt void SCIATx_ISR(void)
{
DINT;
com_send_irq();
SciaRegs.SCIFFTX.bit.TXFFINTCLR = 1; // Clear SCI Interrupt flag
PieCtrlRegs.PIEACK.all = PIEACK_GROUP9; // Issue PIE ACK
}
__interrupt void SCIARx_ISR(void) //UART for debug
{
com_recv_irq();
SciaRegs.SCIFFRX.bit.RXFFOVRCLR = 1; // Clear Overflow flag
SciaRegs.SCIFFRX.bit.RXFFINTCLR = 1; // Clear Interrupt flag
PieCtrlRegs.PIEACK.all = PIEACK_GROUP9; // Issue PIE ack
}
/********************example************************/
void comm_test_init(void)
{
com_init(0xA0, 19200);
set_timer(TMR_COMM_DBUG, 5000*MS_T);
set_timer(TMR_COMM_1MS, 1*MS_T);
}
void comm_test(void)
{
static COM_ERRNO err = E_NONE;
const Uint16 send_array[] = {0x12, 0x34, 0x56, 0x78, 0x9A};
ServiceDog();
#if (COM_MASTER_MODE == 1)
com_master_poll();
if(chk_timer(TMR_COMM_DBUG))
{
err = master_request(0xA0, 0x01, master_user_send_data[0], sizeof(send_array), 8000);
set_timer(TMR_COMM_DBUG, 10000*MS_T);
}
if(get_request_result(&err))
{
if(err == E_NONE)
{
//uart_put_byte('#'); // success then send the next frame
err = master_request(0xA0, 0x01, send_array, sizeof(send_array), 8000);
}
set_timer(TMR_COMM_DBUG, 10000*MS_T);
}
#else
com_slave_poll();
#endif
if(chk_timer(TMR_COMM_1MS))
{
timer_expired_1ms();
set_timer(TMR_COMM_1MS, 1*MS_T);
}
}
#ifndef _COMM_H_
#define _COMM_H_
#define COM_BOARDCAST_ADDR 0xAF
#define COM_USER_BUF_SIZE 10
#define COM_MASTER_MODE 1 // 0:slave 1:master
#define COM_SOH_EOT 0 // 0:time delimiter 1:char delimiter
typedef unsigned int U8;
typedef int I8;
typedef unsigned int U16;
typedef int I16;
typedef unsigned long U32;
typedef long I32;
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef enum
{
E_NONE = 0x00,
E_BADRQC,
E_BADADDR,
E_BADVAL,
E_HANDLING,
E_TOUT
} COM_ERRNO;
#if (COM_MASTER_MODE == 1)
typedef COM_ERRNO (*pfunc_master)(U8 saddr, U8 *recv_buf, U8 recv_len);
typedef struct
{
U8 func_code;
pfunc_master pfun_callback;
} FUNC_MASTER;
#else
typedef COM_ERRNO (*pfunc_slave)(U8* recv_buf, U8 recv_len, U8 **send_buf, U8 *send_len);
typedef struct
{
U8 func_code;
pfunc_slave pfun_callback;
} FUNC_SLAVE;
#endif
typedef enum
{
EV_READY,
EV_FRAME_RECV,
EV_EXEC_FUNC,
EV_FRAME_SENT,
EV_EXEC_DONE,
EV_NONE,
} EVENT_TYPE;
extern void comm_test_init(void);
extern void comm_test(void);
extern COM_ERRNO com_init(U8 slave_addr, U32 baud_rate);
extern COM_ERRNO com_slave_poll(void);
extern COM_ERRNO com_master_poll(void);
extern COM_ERRNO master_request(U8 addr, U8 func, U8 *send_data, U8 send_len, U32 tout_ms);
#endif