关于串口接收入队操作,是在串口中断中实现。
void USART2_IRQHandler(void)
{
if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
uint8_t data = USART_ReceiveData(USART2);
queue_push(data);
}
}
/*!
* \brief 添加指令数据
* \detial 串口接收的数据,通过此函数放入指令队列
* \param _data 指令数据
*/
void queue_push(qdata _data)//1个字节
{
qsize pos = (que._head+1)%QUEUE_MAX_SIZE;//qsize--2个字节
if(pos!=que._tail) //非满状态
{
que._data[que._head] = _data;
que._head = pos;
}
}
为了适应我的开发板,我将串口二中断打开进行入队操作。
串口通过中断每次接收一个字节的数据,并将数据放入队列缓存区
再通过主程序中的死循环中不断取数据,拼接指令
while(1)
{
size = queue_find_cmd(cmd_buffer,CMD_MAX_SIZE); //从缓冲区中获取一条指令
if(size>0&&cmd_buffer[1]!=0x07) //接收到指令 ,及判断是否为开机提示
{
ProcessMessage((PCTRL_MSG)cmd_buffer, size); //指令处理
}
else if(size>0&&cmd_buffer[1]==0x07) //如果为指令0x07就软重置STM32
{
__disable_fault_irq();
NVIC_SystemReset();
}
注意:cmd_buffer代表一条完整的指令,通过queue_find_cmd函数取出指令,当有指令数据且非开机指令的时候,送入ProcessMessage函数进行指令处理
这里有个cmd_buffer强转为PCTRL_MSG类型并且定义PCTRL_MSG这个结构体内容为
typedef struct
{
uint8 cmd_head; //帧头
uint8 cmd_type; //命令类型(UPDATE_CONTROL)
uint8 ctrl_msg; //CtrlMsgType-指示消息的类型
uint16 screen_id; //产生消息的画面ID
uint16 control_id; //产生消息的控件ID
uint8 control_type; //控件类型
uint8 param[256]; //可变长度参数,最多256个字节
uint8 cmd_tail[4]; //帧尾
}CTRL_MSG,*PCTRL_MSG;
数据通过强转成这样一个结构体后,利用结构体的偏移特性得知当前使用的是什么控件,并作出相应的处理
接收数据后,单片机处理通过串口返回发送数据给串口屏在hmi_driver.c中实现。
例程定义了一些函数方便编写使用.
#define TX_8(P1) SEND_DATA((P1)&0xFF) //发送单个字节
#define TX_8N(P,N) SendNU8((uint8 *)P,N) //发送N个字节
#define TX_16(P1) TX_8((P1)>>8);TX_8(P1) //发送16位整数
#define TX_16N(P,N) SendNU16((uint16 *)P,N) //发送N个16位整数
#define TX_32(P1) TX_16((P1)>>16);TX_16((P1)&0xFFFF) //发送32位整数
#if(CRC16_ENABLE)
static uint16 _crc16 = 0xffff;
/*!
* \brief 添加CRC16校验
* \param buffer 待校验的数据
* \param n 数据长度,包含CRC16
* \param pcrc 校验码
*/
static void AddCRC16(uint8 *buffer,uint16 n,uint16 *pcrc)
{
uint16 i,j,carry_flag,a;
for (i=0; i<n; i++)
{
*pcrc=*pcrc^buffer[i];
for (j=0; j<8; j++)
{
a=*pcrc;
carry_flag=a&0x0001;
*pcrc=*pcrc>>1;
if (carry_flag==1)
*pcrc=*pcrc^0xa001;
}
}
}
/*!
* \brief 检查数据是否符合CRC16校验
* \param buffer 待校验的数据,末尾存储CRC16
* \param n 数据长度,包含CRC16
* \return 校验通过返回1,否则返回0
*/
uint16 CheckCRC16(uint8 *buffer,uint16 n)
{
uint16 crc0 = 0x0;
uint16 crc1 = 0xffff;
if(n>=2)
{
crc0 = ((buffer[n-2]<<8)|buffer[n-1]);
AddCRC16(buffer,n-2,&crc1);
}
return (crc0==crc1);
}
/*!
* \brief 发送一个字节
* \param c
*/
void SEND_DATA(uint8 c)
{
AddCRC16(&c,1,&_crc16);
SendChar(c);
}
/*!
* \brief 帧头
*/
void BEGIN_CMD()
{
TX_8(0XEE);
_crc16 = 0XFFFF; //开始计算CRC16
}
/*!
* \brief 帧尾
*/
void END_CMD()
{
uint16 crc16 = _crc16;
TX_16(crc16); //发送CRC16
TX_32(0XFFFCFFFF);
}
#else//NO CRC16
#define SEND_DATA(P) SendChar(P) //发送一个字节
#define BEGIN_CMD() TX_8(0XEE) //帧头
#define END_CMD() TX_32(0XFFFCFFFF) //帧尾
#endif
这些已经封装好了的函数在以后的编写使用中能够极大的减轻编写难度