gd32串口中断测试以及简单解包协议实现
目的
串口发送指令进行读写数据并响应
数据以寄存器形式存放,寄存器地址对应数据位置
寄存器地址16bit,采用大端模式 数据大小16bit
需求
- 实现板内数据读写
- 实现串口中断通讯
- 实现串口数据解包
思路
数据读写
根据所需寄存器数量,定义相应长度数组,数组类型为unsgin short
(2字节),采用首地址加寄存器地址的形式直接访问
定义寄存器宽度及地址
#ifndef __ADDR_H
#define __ADDR_H
#define REG_LEGTH 16
#define REG_WIDTH 16
#define WORKMODE 0X1
#define FIRE 0X2
#define TDC 0X3
#endif
定义寄存器数组
uint16_t Register[REG_LEGTH] =
{0x0000, 0x0001, 0x0002, 0x0003,
0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x0010, 0x0011,
0x0012, 0x0013, 0x0014, 0x0015};
定义寄存器读写函数,分别是读取寄存器,写入寄存器以及读取寄存器所有值
/**
* @brief Read Resgister Value
*
* @param addr
* @return uint16_t value
*/
uint16_t ReadRegister(uint16_t addr)
{
uint16_t *p = Register;
return *(p + addr);
}
/**
* @brief Write Register Value
*
* @param addr Register Address
* @param data Value
*/
void WriteRegister(uint16_t addr, uint16_t data)
{
uint16_t *p = Register;
*(p + addr) = data;
}
/**
* @brief Read ALL Register value
*
*/
void ReadAllRegister(void)
{
int i, j;
// uint8_t *p = (uint8_t *)Register;
uint16_t *p = Register;
for (i = 1; i <= REG_LEGTH * REG_WIDTH / 16; i++)
{
printf("0x%04x %s", *p++, (i % 16 == 0) ? "\n" : "");
}
}
串口中断
通过串口中断或者DMA等,将接受的数据存入接收BUFF,然后针对BUFF进行解析。接收BUFF需要自动清空。
串口中断流程
- 配置串口中断优先级
nvic_irq_enable(USART0_IRQn, 0, 0);
- 配置串口参数
- 打开串口接收非空中断
usart_interrupt_enable(USART0, USART_INT_RBNE);
- 定义串口设备结构体,用来存放相关信息
typedef struct
{
uint8_t state; //状态
uint8_t *rxcount; //读取计数
uint8_t *upcount; //解包计数
uint8_t *end; //计数上限
uint16_t addr; //寄存器地址
uint8_t rxbuffer[32];//接收缓冲区
}usart_device_t;
usart_device_t usart_t=
{
.state=0,
.rxcount=usart_t.rxbuffer,
.upcount=usart_t.rxbuffer,
.end=usart_t.rxbuffer+32-1
};
- 定义串口中断函数
void USART0_IRQHandler(void)
{
if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) //字符中断 USART_INTEN_RBNEIE
{
/* receive data */
if(usart_t.rxcount<usart_t.end){
*usart_t.rxcount=(uint8_t)usart_data_receive(USART0);
usart_t.rxcount++;
}
else{USART_ERROR();};
usart_flag_clear(USART0 ,USART_FLAG_RBNE);
}
}
数据解包
数据包示例
帧头1 帧头2 长度 功能码 地址 数据 BCC校验
写寄存器 0xF6 0x05 0x07 0x02 0x0101 0x0A 0x0A
读寄存器 0xF6 0x05 0x07 0x0B 0x0003 0xFC
使用upcount指针,依次判断rxbuffer中的值,找到正确的数据包。
rxcount用来储存接受到的数据到rxbuffer中,并且记录当前数据位置;
upcount用来记录当前读取数据位置,并将读取数据进行解包。
当rxcount和end重合时,缓冲区满,进入USART_ERROR(响应错误代码并置位rxbuffer到初始位)
当upcount未和rxcount重合时,缓冲区内还有未解析数据
当upcount和rxcount重合时,数据解析完毕,置位指针到初始位
void USART_UNPACK(void)
{
while (usart_t.upcount < usart_t.rxcount) //确保解包数据不超过接收到的数据
{
if (usart_t.rxcount != usart_t.rxbuffer) //确保rxbuffer空时不进行解包
{
if ((*usart_t.upcount == 0xF6) && (*(usart_t.upcount + 1) == 0xF5))
{ //校验帧头
// printf("帧头校验成功\r\n");
usart_t.upcount += 3; //指针移向功能码
if (*usart_t.upcount == 0x02)
{
usart_t.addr = (*(usart_t.upcount + 1) << 8) + *(usart_t.upcount + 2); //读取地址
printf("wirte addr = 0x%04x value = 0x%04x \r\n", usart_t.addr, *(usart_t.upcount + 3));
WriteRegister(usart_t.addr, *(usart_t.upcount + 3));
usart_t.upcount += 5; //指针跳出本包数据
usart_t.state = 1;
}
else if (*usart_t.upcount == 0x0B)
{
usart_t.addr = (*(usart_t.upcount + 1) << 8) + *(usart_t.upcount + 2); //读取地址
printf("read addr = 0x%04x value = 0x%04x \r\n", usart_t.addr, ReadRegister(usart_t.addr));
usart_t.upcount += 4; //指针跳出本包数据
usart_t.state = 1;
}
}
else
{
printf("0x%02x ", *usart_t.upcount); //打印接收到的字符,仅供调试使用
usart_t.upcount++; //指针移向下一个数据
}
}
}
/*清空接收缓冲区,指针归位*/
if (usart_t.state == 1)
{
memset(usart_t.rxbuffer, 0, sizeof(usart_t.rxbuffer));
usart_t.rxcount = usart_t.rxbuffer;
usart_t.upcount = usart_t.rxbuffer;
usart_t.state = 0;
}
}
响应
目前只有简单的几个,使用printf先行测试
先定义响应结构体
typedef struct
{
uint8_t head[2]; //帧头
uint8_t len; //长度
uint8_t func; //功能码
uint8_t addr[2]; //寄存器地址
uint8_t val; // 寄存器值
uint8_t bcc; //bcc校验
}usart_send_t;
初始化
usart_send_t usart_send =
{.head={0xf6,0xf5},
};
响应
//写响应
printf("%s%c%c%c", usart_send.head, 0x04, 0x02, 0x0A);
//读响应
printf("%s%c%c%c%c",
usart_send.head,
*(usart_t.upcount + 1),
*(usart_t.upcount + 2),
ReadRegister(usart_t.addr),
0x0A); //简单测试,后续需要检验时应严格封装
//错误相应
printf("%s%c%c", usart_send.head, 0x04, 0x81);