完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
之前写了简单的Bootloader,只实现了程序跳转的功能。但是作为一个Bootloader,只能完成程序跳转感觉缺了点啥。那就继续添加可以加载固件的功能吧。
加载固件的途径有很多,常见的就有串口、可移动存储设备、网络等等。这里就从最简单的开始吧,先做串口的。 整个程序的思路如下:
1 Flash写入程序 1.0 目标 将一块RAM中的数据写入到Flash。 1.1 准备硬件 依然是淘宝上随处可见的STM32F103C8T6核心板,以及烧录和调试需要用到的STLINK。 1.2 理论基础 Flash简介之类的东西就不写了,上网查一下都有。这里主要写这款硬件相关的吧。这里主要参考ST的PM0075文档《STM32F10xxx Flash memory microcontrollers》,感兴趣的可以去看原文。 F103C8属于STMF1系列中的中等容量单片机,Flash容量为64KB,占用地址为:0x08000000~0x0800FFFF。这块64KB的Flash被分为64个1KB的页。这就意味着擦除的最小单位为1KB。 了解完Flash的组织结构后,再来了解一下Flash的读写操作。 读操作相对简单,直接声明一个指向要读取数据的指针就可以直接读取数据了。 写操作相对繁琐,ST官方PM0075文档中有如下流程图: 对照着流程图,这里稍作一点解释。
还有一点需要注意的。Flash操作的带宽为16位(16bit)。这意味着我们每次写入的时候都只能是16位的倍数。但是HAL库会提供写8位的函数,实际上这个函数是把高八位设定为0而已,本质上还是写入16位。 1.3 创建工程 创建工程就比较简单了。由于HAL库的工程会带有Flash操作的函数库,所以我们正常创建一个LED闪烁的工程即可。如下图所示: 1.3.1 简单测试 首先简单测试一下上面提到的读写操作是否有问题。 读操作: /* * * 从Flash的某个地址中读取一个半字(16bit) * */ uint16_t FLASH_ReadHalfWord(uint32_t addr) { return *(volatile uint16_t *)addr; } 由于写入是半字写入,那读取也写成一致的半字读取。需要字读取的可以在半字读取的基础上进行改写。 写操作函数不需要自己写,Hal库有提供。但是需要注意,执行的操作还是要按照上述流程图的顺序来执行的。 下面是Hal库提供的函数。 HAL_StatusTypeDef HAL_FLASH_Unlock(void); //解锁 HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data); //编程 HAL_StatusTypeDef HAL_FLASH_Lock(void); //上锁 使用上述的函数,可以完成写入半字的操作,如下: /* * * 按照官方文档的写入序列,向Flash的某个地址写入半字(16bit) * */ uint8_t Flash_WriteHalfWord(uint32_t addr, uint16_t data) { HAL_StatusTypeDef ret; //操作返回值 /* 1、解锁 */ ret = HAL_FLASH_Unlock(); if(ret != HAL_OK) { return 0; } /* 2、设置PG位为1 */ /* 3、写入半字 */ /* 4、等待写入完成 */ ret = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr, data); if(ret != HAL_OK) { return 0; } /* 5、读出,与写入的数据进行比较 */ uint16_t readBackData = Flash_ReadHalfWord(addr); if(readBackData != data) { return 0; } /* 6、上锁 */ ret = HAL_FLASH_Lock(); /* 写入成功 */ return 1; } 其中HAL库的HAL_FLASH_Program()函数完成的工作比较多,包括: 等待BSY位为0 设置PG位为1 往指定的地址中写入数据 等待BSY位为0 清除PG位为0 这样我们就得到了往Flash中写入半字的函数。但是这里还有一个问题,在写入操作之前,没有擦除目标地址所在的页。 Flash与EEPROM有个不同点,Flash是不可改写的。如果目标地址里面存在数据,那么再次写入数据时会失败。因此需要在写入数据之前,擦除目标地址所在的页。HAL为我们提供了擦除函数,如下: /* * description : flash擦除操作 * param - pEraseInit : 擦除初始结构体,用于设置擦除类型、地址、大小。 * param - PageError : 擦除失败时,保存擦除失败发生时的地址。 * return : 操作结果 */ HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError); 至此可以编写一个简单的测试程序,主要代码如下。每秒中擦除0x08005000地址所在的块,然后写入一个数据。 现象:可以看到LED灯每秒闪烁一次,也可以在调试中查看0x08005000地址的值在变化。 FLASH_EraseInitTypeDef flashEraseStrc; uint32_t pageErr; while (1) { data += 100; if(data > 65535) data = 0; flashEraseStrc.TypeErase = FLASH_TYPEERASE_PAGES; flashEraseStrc.PageAddress = 0x08005000; flashEraseStrc.NbPages = 1; HAL_FLASHEx_Erase(&flashEraseStrc, &pageErr); if(Flash_WriteHalfWord(0x08005000, data) >= 0) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); } HAL_Delay(1000); } 1.3.2 复制函数 完成Flash的读写函数后,可以继续进行下去,编写一个从RAM复制数据到Flash的函数。如下: /* * description : 从RAM复制到Flash * param - source : 源地址 * param - destination : 目的地址 * param - size : 复制字节数 * return : 0 - false; 1 - true; * note : Flash的组织形式和Bin文件的组织形式都是小端,所以是低8位在前。 */ uint8_t Flash_RamToFlash(uint8_t *source, uint32_t destination, uint32_t size) { uint16_t u16Temp = 0; //16位临时值 uint32_t i = 0; //循环计数值 uint8_t ret = 0; //操作结果值 for(i=0; i if((i+1) >= size) { /* 最后一个单数字节,在其低8位补0xFF */ u16Temp = (0xFF << 8) + source; } else { /* 正常能凑成双字节(半字)的数据 */ u16Temp = (source[i+1] << 8) + source; } ret = Flash_WriteHalfWord(destination + i, u16Temp); if(ret == 0) return 0; } return 1; } 完成这个函数之后,其实就已经完成了向Flash写入程序了。因为上位机发送过来的块数据也是先存储到RAM再写入Flash的。 2 上位机及单片机串口接收程序 2.0 目标 上位机与单片机通过串口进行通讯,先握手,再传输一些必要的信息,最后完成Bin文件的传输。 2.1 创建工程 单片机工程不需要重建,沿用上面的就行。上位机比较简单,创建一个.NET Winform的C#工程即可。创建好工程后,摆放一下控件,最终效果如下所示: 2.2 上位机主要逻辑 这个上位机的功能比较简单,这里简要描述一下工作逻辑。 2.2.1加载一个Bin文件。 这里使用常见的文件路径+浏览按键的方式。单击浏览按键,弹出一个打开文件对话框(OpenFileDialog),OpenFileDialog的对象设置一下属性,使其过滤器只能识别Bin文件。选择好文件后将路径名显示在长条文本框内。同时创建一个BinaryReader对象,读取Bin文件的内容,并显示到大文本框内。核心代码如下: /// 2.2.2 上位机串口程序 写串口通讯程序之前,要先要先确定通讯的协议。考虑到通讯不算复杂,就设计一个定长帧协议好了。(此处留空。协议表留在办公室忘记上传了) 串口控件的程序用的是以前写的串口封装类。这里稍微说一下工作的逻辑。由于串口接收事件有大约十几MS的延迟,因此想要实现单片机上接收一个字节处理一个字节的逻辑基本是做不到的。因此需要在每次发生串口接收事件时,将串口接收到的数据全部填充到一个缓冲区中。在需要读取串口数据时,再使用帧结构的规则来检索缓冲区,最后得到需要的数据。 2.2.3 上位机通讯过程
3 总结 使用串口加载固件的重点其实不是怎么传输,而是怎么编程Flash。支持IAP(应用内编程)的芯片很多,操作方法也很多,一般来说,阅读官方的文档都能解决问题。 联系方式:489304195@qq.com |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1529 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1482 浏览 1 评论
900 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
644 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1523 浏览 2 评论
1839浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
584浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
487浏览 3评论
489浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
470浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-1 09:22 , Processed in 0.790223 second(s), Total 76, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号