—基于GD32E230-OTA的学习过程(一)
OTA的定义和基本实现思路
OTA(Over-The-Air)指的是通过无线方式将新固件程序传输到设备并进行更新的技术。OTA的主要目的是方便远程更新设备固件,而无需通过有线连接或人工干预。以下是OTA的基本实现思路:
OTA的定义
OTA实际上是通过无线方式接收新固件程序并进行更新的程序。为了实现无线升级的功能,通常在无线通信上使用MQTT协议。MQTT是一种轻量级的消息传输协议,适用于低带宽和不可靠网络环境,非常适合嵌入式设备的通信需求。
基本实现思路
在实现OTA过程中,需要对设备的FLASH存储进行合理划分,通常会分为三个区域:
Bootloader:用于启动设备并检查是否有新固件的小程序。Bootloader负责在设备上电或重启时运行,并决定是启动当前固件还是新固件。
Current Firmware(当前固件):存储当前运行的固件程序。
New Firmware(新固件):用于存储从服务器上下载的新固件程序。
要实现OTA,实际上我们需要完成以下三个主要模块:
MQTT连接:通过MQTT协议与服务器建立连接,接收新固件的更新通知和数据传输。
编写Bootloader程序:Bootloader程序负责在设备启动时检查是否有新固件,并根据检查结果决定启动当前固件还是新固件。如果有新固件,则将新固件移动到当前固件区域并启动。
实现跳转到运行程序:当Bootloader决定启动某个固件时,需要正确跳转到该固件的入口地址并开始执行。
MQTT连接
一、实现串口发送信息
毕竟要进行固件的读写,所以对数据的读写要求比较高,所以这里使用了循环串口读取、写入消息的写法。
初始化不再展示,主要就是配置串口,使能空闲中断。
串口循环参数如下面展示的结构体:
typedef struct{
uint16_t Usart_RxCounter;//接收数据计数
uint16_t Usart_TxCounter;//发送数据计数
uint8_t Usart_TxCpltFlag;//发送完成标志位
Usart_LocationCB UsartTxLocation[TX_NUM]; //标记数据开始、结束结构体的数组
Usart_LocationCB *UsartTxDataInPtr; //插入数组的指针
Usart_LocationCB *UsartTxDataOutPtr; //提取数组的指针
Usart_LocationCB *UsartTxDataEndPtr; //结尾数组的指针
Usart_LocationCB UsartRxLocation[TX_NUM]; //标记数据开始、结束结构体的数组
Usart_LocationCB *UsartRxDataInPtr; //插入数组的指针
Usart_LocationCB *UsartRxDataOutPtr; //提取数组的指针
Usart_LocationCB *UsartRxDataEndPtr; //结尾数组的指针
}Usart_ControlCB;
串口缓冲区初始化如下:
/*
**brief:串口0缓冲区初始化
**return:none
*/
void Usart0_Buff_Init()
{
Usart0_ControlHandler.UsartTxDataInPtr = &Usart0_ControlHandler.UsartTxLocation[0];//插入指针取数组第0位的地址
Usart0_ControlHandler.UsartTxDataOutPtr = Usart0_ControlHandler.UsartTxDataInPtr; //提取指针指向插入指针的位置
Usart0_ControlHandler.UsartTxDataEndPtr = &Usart0_ControlHandler.UsartTxLocation[TX_NUM- 1];//结尾指针指向最末位数组
Usart0_ControlHandler.UsartTxDataInPtr->StartLocation = TxBuff;//插入指针的开始标志钉在传输缓冲区的开始位置
Usart0_ControlHandler.Usart_TxCounter = 0;//传输计数器清零
Usart0_ControlHandler.Usart_TxCpltFlag = RESET;//
Usart0_ControlHandler.UsartRxDataInPtr = &Usart0_ControlHandler.UsartRxLocation[0];//插入指针取数组第0位的地址
Usart0_ControlHandler.UsartRxDataOutPtr = Usart0_ControlHandler.UsartRxDataInPtr; //提取指针指向插入指针的位置
Usart0_ControlHandler.UsartRxDataEndPtr = &Usart0_ControlHandler.UsartRxLocation[TX_NUM- 1];//结尾指针指向最末位数组
Usart0_ControlHandler.UsartRxDataInPtr->StartLocation = RxBuff;//插入指针的开始标志钉在传输缓冲区的开始位置
Usart0_ControlHandler.Usart_RxCounter = 0;//传输计数器清零
usart_interrupt_flag_clear(BSP_USART,USART_INT_FLAG_IDLE); //空闲中断清零
usart_interrupt_enable(BSP_USART,USART_INT_IDLE); // 空闲检测中断
}
串口发送缓冲区管理函数如下:
/*
**brief:串口0缓冲区管理
**param[in]:*data-所要接收的数据的开头指针
**param[in]:datalen-所要接收的数据的长度
**return:none
*/
void Usart0_Buff_Data(uint8_t *data,uint16_t datalen)
{
//如果缓冲区长度-已经接收的长度=剩余长度>要接收的数据的长度
if(TxBuffSize - Usart0_ControlHandler.Usart_TxCounter > datalen){
//接收开始指针指向缓冲区的Usart_TxCounter位
Usart0_ControlHandler.UsartTxDataInPtr->StartLocation = &TxBuff[Usart0_ControlHandler.Usart_TxCounter];
}
else{
//否则,接收计数清零,接收开始指针指向缓冲区的第0位
Usart0_ControlHandler.Usart_TxCounter = 0;
Usart0_ControlHandler.UsartTxDataInPtr->StartLocation = TxBuff;
}
memcpy(Usart0_ControlHandler.UsartTxDataInPtr->StartLocation,data,datalen);//拷贝数据
Usart0_ControlHandler.Usart_TxCounter += datalen;//计数器在原来基础上+datalen
Usart0_ControlHandler.UsartTxDataInPtr->EndLocation = &TxBuff[Usart0_ControlHandler.Usart_TxCounter];//结尾指针指向TxBuff[Usart0_ControlHandler.Usart_TxCounter]
Usart0_ControlHandler.UsartRxDataInPtr++;//插入指针+1
//如果插入数据区已经到了最后,那么重新指向0,覆写数据
if(Usart0_ControlHandler.UsartRxDataInPtr == Usart0_ControlHandler.UsartTxDataEndPtr){
Usart0_ControlHandler.UsartRxDataInPtr = &Usart0_ControlHandler.UsartTxLocation[0];
}
}
一个简单的用来测试的发送函数:
/*-------------------------------------------------*/
/*函数名:串口0 发送数据 函数 */
/*返回值:无 */
/*-------------------------------------------------*/
void Usart0_Send(uint8_t *data, uint16_t datalen)
{
//将指针赋予data_ptr
uint8_t *data_ptr = data;
//长度大于0
while (datalen > 0) {
//一直等到发送寄存器空闲
while (!usart_flag_get(BSP_USART, USART_FLAG_TBE)){};
//发送数据
usart_data_transmit(BSP_USART, *data_ptr++);
//长度--
--datalen;
}
}
组合一下就变成了
/*-------------------------------------------------*/
/*函数名:串口0 发送数据 函数-----------------------*/
**param[in]:*data-所要接收的数据的开头指针----------*/
**param[in]:datalen-所要接收的数据的长度------------*/
/*返回值:无 */
/*-------------------------------------------------*/
void Usart0_Buff_And_Send_Data(uint8_t *data, uint16_t datalen)
{
// 如果缓冲区长度 - 已经接收的长度 > 要接收的数据的长度
if (TxBuffSize - Usart0_ControlHandler.Usart_TxCounter > datalen) {
// 接收开始指针指向缓冲区的 Usart_TxCounter 位
Usart0_ControlHandler.UsartTxDataInPtr->StartLocation = &TxBuff[Usart0_ControlHandler.Usart_TxCounter];
}
else {
// 接收计数清零,接收开始指针指向缓冲区的第 0 位
Usart0_ControlHandler.Usart_TxCounter = 0;
Usart0_ControlHandler.UsartTxDataInPtr->StartLocation = TxBuff;
}
// 拷贝数据到缓冲区
memcpy(Usart0_ControlHandler.UsartTxDataInPtr->StartLocation, data, datalen);
Usart0_ControlHandler.Usart_TxCounter += datalen; // 计数器在原来基础上 + datalen
Usart0_ControlHandler.UsartTxDataInPtr->EndLocation = &TxBuff[Usart0_ControlHandler.Usart_TxCounter]; // 结尾指针指向新的缓冲区位置
Usart0_ControlHandler.UsartRxDataInPtr++; // 插入指针 +1
// 如果插入数据区已经到了最后,那么重新指向 0 覆写数据
if (Usart0_ControlHandler.UsartRxDataInPtr == Usart0_ControlHandler.UsartTxDataEndPtr) {
Usart0_ControlHandler.UsartRxDataInPtr = &Usart0_ControlHandler.UsartTxLocation[0];
}
// 将数据从缓冲区发送出去
uint8_t *send_ptr = Usart0_ControlHandler.UsartTxDataInPtr->StartLocation;
while (datalen > 0) {
// 一直等到发送寄存器空闲
while (!usart_flag_get(BSP_USART, USART_FLAG_TBE)) {};
// 发送数据
usart_data_transmit(BSP_USART, *send_ptr++);
// 长度--
--datalen;
}
}
测试效果如下:
能成功打印多次字符串而且不会报错