基于GD32E230的远程OTA的学习过程(一)

—基于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;
    }
}

测试效果如下:
能成功打印
能成功打印多次字符串而且不会报错

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值