您的位置:首页 > 其它

基于串口的简单通信协议

2013-11-04 23:32 645 查看
发一个基于串口的简单通信协议(在TMS320F2812上实现)

可变帧长

可选的CRC校验

基于状态机的接收机制

/*
*          ====    SciA模块实现串口      ====
*
*  使用函数Sci_puts()函数发送unsigned char[] 数组
*  设置回调函数set_RXAHook()接收串口数据
*
*      Author: hexingshi
*      Date:   2012-2-25
*/

#include "lib/DSP28_Device.h"

void Init_Gpio_Sci();
void Init_SciDevice(long baud);
interrupt void SciRXA_ISR(void);
interrupt void SciTXA_ISR(void);
void Sci_puts(unsigned char * str, unsigned char num);

/*
*      ==  初始化SciA模块   ==
*  使用前必须使能SciA时钟       SysCtrlRegs.PCLKCR.bit.SCIENCLKA = 1;
*  LSPCLK时钟必须为37.5MHz      SysCtrlRegs.LOSPCP.all = 0x0002;
*/
void Init_Sci(long baud)
{
Init_Gpio_Sci();
Init_SciDevice(baud);

EALLOW;
PieVectTable.TXAINT = &SciTXA_ISR;
PieVectTable.RXAINT = &SciRXA_ISR;
EDIS;

PieCtrlRegs.PIEIER9.bit.INTx1=1;  //使能PIE模块中的SCI接收中断
PieCtrlRegs.PIEIER9.bit.INTx2=1;  //使能PIE模块中的SCI发送中断
IER|=M_INT9;  //开CPU中断
}

/*
*      ==  设置SciA模块功能引脚    ==
*  SCITXDA_GPIOF4  SCIA的发送引脚
*  SCIRXDA_GPIOF5  SCIA的接收引脚
*/
void Init_Gpio_Sci()
{
EALLOW;
GpioMuxRegs.GPFMUX.bit.SCITXDA_GPIOF4=1;  //设置SCIA的发送引脚
GpioMuxRegs.GPFMUX.bit.SCIRXDA_GPIOF5=1;  //设置SCIA的接收引脚
EDIS;
}

/*
*      ==  配置SciA模块为串口 ==
*  配置SciA波特率
*  LSPCLK时钟必须为37.5MHz      SysCtrlRegs.LOSPCP.all = 0x0002;
*/
void Init_SciDevice(long baud)
{
long brr = 0;

SciaRegs.SCICCR.bit.STOPBITS=0;        //1位停止位
SciaRegs.SCICCR.bit.PARITYENA=0;       //禁止极性功能
SciaRegs.SCICCR.bit.LOOPBKENA=0;       //禁止回送测试模式功能
SciaRegs.SCICCR.bit.ADDRIDLE_MODE=0;   //空闲线模式
SciaRegs.SCICCR.bit.SCICHAR=7;         //8位数据位

SciaRegs.SCICTL1.bit.TXENA=1;          //SCIA模块的发送使能
SciaRegs.SCICTL1.bit.RXENA=1;          //SCIA模块的接收使能

brr = 4687500/baud  -1 ;
SciaRegs.SCIHBAUD=brr>>8;
SciaRegs.SCILBAUD=brr%0x100;                //波特率为19200

SciaRegs.SCICTL2.bit.RXBKINTENA=1;     //SCIA模块接收中断使能
SciaRegs.SCICTL2.bit.TXINTENA=1;       //SCIA模块发送中断使能
SciaRegs.SCICTL1.bit.SWRESET=1;        //重启SCI
}

static unsigned char TxNum = 0;
static volatile char TxBusy = 0;        //表示缓冲区中的数据状态,1=正在发送数据,0=数据已经发送完成
static unsigned char * TxStr;
/*
*      ==  串口数据发送函数    ==
*  以中断的方式自动发送str数组
*  函数立即返回
*  如果上一次发送未完成,函数将阻塞,直到上一次发送完成
*/
void Sci_puts(unsigned char * str, unsigned char num)
{
while(TxBusy != 0) NOP; //等待数据发送完
TxBusy = 1;
TxNum = num-1;
SciaRegs.SCITXBUF= *str;
str++;
TxStr = str;
}
interrupt void SciTXA_ISR(void)     // SCI-A发送中断函数
{
if(TxNum > 0)
{
SciaRegs.SCITXBUF= *TxStr; //发送数据
TxStr++;
TxNum--;
}else
TxBusy = 0;

PieCtrlRegs.PIEACK.all=0x0100;  //使得同组其他中断能够得到响应
}

static void (*RXA_hook)(unsigned char val) = 0;
interrupt void SciRXA_ISR(void)     // SCI-A接收中断函数
{
RXA_hook((unsigned char)SciaRegs.SCIRXBUF.bit.RXDT);
PieCtrlRegs.PIEACK.all=0x0100;  //使得同组其他中断能够得到响应
}
/*
*      ==  串口数据接收函数    ==
*  使用串口接收中断,通过回调函数进行接收
*  必须设置回调函数RXA_hook
*/
void set_RXAHook(void (*f)(unsigned char val))
{
RXA_hook = f;
}

/*
*          ====    基于串口的简单通信协议 ====
*
*  帧格式:        起始位 |   数据长度 | 数据... | 校验 |
*  起始位         0xAA
*  校验方式        CRC16循环校验  低8位在前,高8位在后
*
*  串口提供:   发送数据函数,以unsigned char[]数组格式
*              接收数据回调函数
*
*  通过使用宏TxBufferMaxNum 定义发送缓冲区大小
*  通过使用宏RxBufferMaxNum 定义接收缓冲区大小
*  数据长度最长为255
*  使用前必须调用初始化函数ComPro_Init()
*
*      Author: hexingshi
*      Date:   2012-4-25
*/

unsigned int CRC16_Cal(unsigned char * data, unsigned char len);
void Sci_puts(unsigned char * str, unsigned char num);
void set_RXAHook(void (*f)(unsigned char val));
static void ComPro_Rx(unsigned char val);
void ComPro_Received();

#define TxBufferMaxNum  56
#define RxBufferMaxNum  56
static unsigned char TxBuffer[TxBufferMaxNum];          //发送缓冲区
static unsigned char RxBuffer[RxBufferMaxNum];          //接收缓冲区
static unsigned char RxNum = 0;                         //接收缓冲区中的数据个数
static unsigned int  RxCRC = 0;                         //接收到的数据帧中的校验位

/*
*      ==  发送数据    ==
*  将数据打包,并通过串口发送
*  函数立即返回
*  如果Sci_puts上一次调用未完成,会产生阻塞
*/
void ComPro_put(unsigned char * data, unsigned char num)
{
unsigned char i;
unsigned int crc;

TxBuffer[0] = 0xAA;             //设置起始位
TxBuffer[1] = num;              //设置数据长度位
for (i = 0; i < num; ++i) {
TxBuffer[i+2] = data[i];
}

crc = CRC16_Cal(data, num);
TxBuffer[num+2] = (unsigned char)(crc&0x00FF);      //crc的低8位
TxBuffer[num+3] = (unsigned char)(crc>>8);            //crc的高8位

Sci_puts(TxBuffer, num+4);
}

/*
*      ==  通信协议初始化 ==
*  设置底层串口接收数据的回调函数
*/
void ComPro_Init()
{
set_RXAHook(ComPro_Rx);
}

/*
*      ==  串口的回调函数 ==
*  当串口收到数据后回调此函数
*  使用前必须设置串口hook函数
*  通过状态机获取数据帧
*/
static void ComPro_Rx(unsigned char val)
{
static unsigned char RxState = 0;
static unsigned char RxBufferIndex = 0;

switch (RxState)
{
case 0:
if(val == 0xAA) RxState = 1;
break;
case 1:
if(val > RxBufferMaxNum)
{
RxState = 0;
}
else
{
RxState = 2;
RxNum = val;
RxBufferIndex = 0;
}
break;
case 2:
RxBuffer[RxBufferIndex] = val;
RxBufferIndex++;
if (RxBufferIndex >= RxNum)  RxState = 3;
break;
case 3:
RxCRC = val;
RxState = 4;
break;
case 4:
RxCRC = ((unsigned int)val)*256 + RxCRC;
RxState = 0;
ComPro_Received();
break;
}
}

/*
*      ==  数据接收完成hook  ==
*  数据接收完成后,可以在此函数中添加处理
*  CRC16校验:调用CRC16_Cal()与接收的校验位RxCRC比较
*/
void ComPro_Received()
{
//添加对数据的处理语句
ComPro_put(RxBuffer, RxNum);    //测试 :回传数据
}

这个简单的通信协议可以选择加入计时器后,设置超时时间,然后让状态机到0状态。

printf
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: