目的

串口发送指令进行读写数据并响应

数据以寄存器形式存放,寄存器地址对应数据位置

寄存器地址16bit,采用大端模式 数据大小16bit

需求

  1. 实现板内数据读写
  2. 实现串口中断通讯
  3. 实现串口数据解包

思路

数据读写

根据所需寄存器数量,定义相应长度数组,数组类型为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需要自动清空。

串口中断流程

  1. 配置串口中断优先级 nvic_irq_enable(USART0_IRQn, 0, 0);
  2. 配置串口参数
  3. 打开串口接收非空中断usart_interrupt_enable(USART0, USART_INT_RBNE);
  4. 定义串口设备结构体,用来存放相关信息
    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
    };
  5. 定义串口中断函数
    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);