这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界» 论坛首页» 嵌入式开发» MCU» 【dsPIC33E】Bootloader(三)Bootloader下位机

共5条 1/1 1 跳转至

【dsPIC33E】Bootloader(三)Bootloader下位机

助工
2021-08-12 23:56:33 打赏

前面2节讲述了dsPIC33E的Flash存储结构以及Hex格式,接下来开始讲述如何编写Bootloader下位机和上位机。


本节讲述下位机的设计,考虑到执行效率和烧录时间,我们将主要解析工作放到上位机中,因为PC的执行速度远高于MCU。在上位机中,我们将Hex文件解析成一组一组的地址和数据组合,然后将一个地址和数据打包发送到Bootloader,Bootloader将其写入对应地址内。


我们先将地址空间分割成三段区域:


0x000000-0x0007FF: 第一段为起始跳转、复位和中断向量表


0x000800-0x0071FF: 第二段为Bootloader,此处预留0x6400长度,为了以后扩充,多预留了一点,后面再空一点点。


0x008000-0x02A800: 第三段为User App,注意最后一页(包含配置字)不要使用,擦除时会导致代码保护清零。


分别在Bootloader和AfterBootloader(即User App)项目中添加gld文件(从xc16安装文件夹下复制到项目目录下),修改对应的程序地址(修改program (xr)的起始地址和长度),这样编译时,会限制程序起始和结束地址。


以下我们将使用具体项目来说明,使用的芯片为dsPIC33EP256GP506


示例项目下载:https://download.csdn.net/download/u010875635/10819819

Bootloader中,最重要的就是2个功能,一是通信,二是Flash读写。由于不能使用中断,防止中断向量表与User App的冲突,通信都采用查询模式。开发板正好有现成的串口,本例采用串口通信,考虑到后面可能会使用CAN,因此核心数据长度为8bytes,串口本身校验功能差,我们再在前后分别增加2个字节,总计12个字节。


此例总共设有5中命令,兼容Bootloader和User App,分别为:EntryBootloader(User App中可用)、Reset、Data、DataEnd、CheckBootloader、Erase。


EntryBootloader,Bootloader不适用,User App中可用,由上过机发送过来的命令,用于在User App中复位到Bootloader,以便于进行代码烧写工作;Bootloader启动时会向上位机发送EntryBootloader回馈,表示进入Bootloader。


Reset,Bootloader中可用,上位机发送过来的命令,用于烧写完毕后,重启系统,User App启动时会向上位机发送Reset回馈表示已经于User App。


Data,接收到的为数据,由上位机发送过来,Bootloader将其写入flash。


DataEnd,上位机发送数据完毕,告诉Bootloader系统可以复位了。


CheckBootloader,上位机发送的命令,用于检查是否在Bootloader中,若在则回馈,若不再不回馈,上位机有超时判断。


Erase,上位机发送命令,下位机接收到命令后,会擦除所有用户程序相关的存储区,包括复位和中断向量表。


#include "McuDrivers/uart/uart.h"

#include "McuDrivers/system/system.h"

#include "McuDrivers/flash/DataRecord.h"

#include "McuDrivers/gpio/gpio.h"

#include "McuDrivers/timer/timer.h"

#include "Protocols/Bootloader.h"

#include "Protocols/UserAppFlash.h"

#include "McuDrivers/flash/InnerFlash.h"

uint8_t receiveData[12]={0},dataIndex=0;

void Uart_Tasks();

uint8_t Time_Tasks();

uint8_t Tick_Tasks();

int main(void)

{

uint8_t run=1;

System_Initialize_g();

Uart_Initial_g(115200);

System_Delay_MS_g(100);

GPIO_LED_D4_Enable_g();

GPIO_LED_D3_Enable_g();

while(run)

{

Uart_Tasks(); //接收数据处理

run = Tick_Tasks();

}

while(1)

{

Uart_Tasks(); //接收数据处理

}

return 0;

}

uint16_t countTick = 0;

uint8_t Tick_Tasks()

{

countTick++;

//晶振为80MHz

//每隔800大约1ms

if(countTick==800)

{

countTick=0;

return Time_Tasks();

}

return 1;

}

uint16_t countTimer=0;

uint8_t Time_Tasks()

{

countTimer++;

if(countTimer==4000)

{

BOOTLOADER_GOTO_8000();

return 0;

}

if(countTimer>4000)

return 0;

if(countTimer%500==0)

GPIO_LED_D3_Toggle_g();

return 1;

}

//串口接收任务

void Uart_Tasks()

{

if(U2STAbits.URXDA==1)

{

uint8_t tmp = Uart_ReadByte();

switch(dataIndex)

{

case 0:

if(tmp==g_Bootloader_Receive_StartFlag[0]) //起始标识1

{

receiveData[0] = g_Bootloader_Receive_StartFlag[0];

dataIndex = 1;

}

else

dataIndex = 0;

break;

case 1:

if(tmp==g_Bootloader_Receive_StartFlag[1]) //起始标识2

{

receiveData[1] = g_Bootloader_Receive_StartFlag[1];

dataIndex = 2;

}

else

dataIndex = 0;

break;

case 10:

if(tmp==g_Bootloader_Receive_EndFlag[0])

{

receiveData[dataIndex] = g_Bootloader_Receive_EndFlag[0];

dataIndex = 11;

}

else

dataIndex = 0;

break;

case 11:

if(tmp==g_Bootloader_Receive_EndFlag[1])

{

receiveData[dataIndex] = g_Bootloader_Receive_EndFlag[1];

AfterBootloader_CmdType result = Bootloader_DataParse(receiveData,12);

//Uart_WriteByte_g(result);

switch(result)

{

case EntryBootloader:

break;

case Reset:

BOOTLOADER_SOFTWARE_RESET();

break;

case Data: //数据

countTimer = 4001; //出现数据即不跳转到用户程序,避免烧录用户程序时出现异常

//UserFlash_EraseIvtAndUserAppBlock();

UserFlash_DataParseAddrData(receiveData,12);

Uart_WriteBytes_g(g_Bootloader_DataReponse,12);

GPIO_LED_D3_Toggle_g();

break;

case DataEnd: //数据结束

GPIO_LED_D4_Enable_g();

GPIO_LED_D3_Enable_g();

Uart_WriteBytes_g(g_Bootloader_DataProgramEndReponse,12);

BOOTLOADER_SOFTWARE_RESET(); //数据刷新完毕,复位

break;

case CheckBootloader: //回馈正处于Bootloader中

Uart_WriteBytes_g(g_Bootloader_CheckBootloaderReponse,12);

break;

case Erase:

UserFlash_EraseIvtAndUserAppBlock();

Uart_WriteBytes_g(g_Bootloader_EraseFlashReponse,12);

break;

default:

break;

}

}

dataIndex = 0;

break;

default:

receiveData[dataIndex] = tmp; dataIndex++;

break;

}

}

}


在数据处理上,由于dsPIC33EP256GP506只支持双指令字编程,所以我们接收到2个数据才会判断然后写入flash,考虑到最后肯定是配置字,而此处配置字是无法写入的,所以不考虑数据个数是否是奇数(奇数会导致最后一个数据漏写)情况了。




专家
2021-08-13 11:35:50 打赏
2楼

学习学习


工程师
2021-08-13 23:47:17 打赏
3楼

非常不错的分享


高工
2021-08-15 23:58:09 打赏
4楼

不错的机器


菜鸟
2023-02-20 11:52:55 打赏
5楼

很不错,感谢楼主


共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册]