目的
串口發送指令進行讀寫數據並響應
數據以寄存器形式存放,寄存器地址對應數據位置
寄存器地址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);
