gd32串口中斷測試以及簡單解包協議實現


目的

串口發送指令進行讀寫數據並響應

數據以寄存器形式存放,寄存器地址對應數據位置

寄存器地址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
};
  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);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM