基于串口的简单通信协议

发一个基于串口的简单通信协议(在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

你可能感兴趣的:(C++)