一、Bootloader固件升级
RT-Thread 通用 Bootloader 的软件框架
RT-Thread 通用 Bootloader 特点
- 以 bin 文件的形式提供,无需修改即可使用
- 资源占用小,ROM 最小只需要 16KB,最大 32KB
- 适用于多系列 STM32 芯片(目前支持 F1 和 F4 系列 )
- 支持各种 SPI Flash 存储固件
- 支持固件加解密功能
- 支持多种固件压缩方式
- 支持恢复出厂固件功能
1.固件升级过程
当系统需要升级固件时,Bootloader 将从 download
分区将固件搬运到 app
分区,主要功能流程如下所示:
- Bootloader 启动时检查
download
分区和app
分区中的固件版本。 - 如果两个固件版本相同,则跳转到 app 分区,Bootloader 运行结束。
- 固件版本不同则将
download
分区中的固件搬运到app
分区。 - 在搬运的过程中 Bootloader 可以对固件进行校验、解密、解压缩等操作。
- 搬运完毕后,删除
download
分区中存储的固件。 - 重启系统跳转到
app
分区中的固件运行,Bootloader 运行结束。
2.获取 Bootloader
根据板级实际情况选择硬件配置和分区
3.工程设置分区表
/* partition table */
#define FAL_PART_TABLE \
{ \
{FAL_PART_MAGIC_WROD, "download", "onchip_flash_128k", 0 , (128*1024), 0}, \
{FAL_PART_MAGIC_WROD, "app", "onchip_flash_128k", (1*128*1024) , FLASH_SIZE_GRANULARITY_128K-(128*1024), 0}, \
}
//onchip_flash_128k代表首地址为地址0x08020000基础,偏移0, 长度为(128*1024),即128k
二、example程序使用
将示例程序添加到bsp/xxx/application文件夹下,工程文件夹路径下,并在SConscript文件中加入其文件,在编译时或生成工程时,就会将文件加入工程之中执行编译
-
application的SConscript文件
import rtconfig from building import * cwd = GetCurrentDir() CPPPATH = [cwd, str(Dir('#'))] src = Split(""" application.c startup.c """) # add UI engine demo. if GetDepend('PKG_USING_GUIENGINE'): src += ['rtgui_demo.c'] src += ['tofile.c'] #将需要添加的文件加入src变量中 src += ['rtc_test.c'] group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) Return('group')
通过FINSH_FUNCTION_EXPORT或MSH_CMD_EXPORT 宏定义,可以将函数加入到Finsh或MSH的命令之中
1.使用Ymodem协议传输文件到RT-Thread文件系统
1)使能Ymodem组件
- 开启RT-Thread Components ---> Utilities ---> Enable Ymodem
2)添加tofile.c应用程序
- 将rt-thread\examples\ymodem\tofile.c文件添加到bsp工程的application文件夹中,并在其SConscript文件中添加tofile.c
3)修改文件默认保存路径
-
tofile.c默认使用根目录/保存文件,但ST的SPI Flash关连的文件系统挂载在/spi路径下,所以需要将修改文件保存路径,否则无法生成文件
static enum rym_code _rym_bg( struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len) { struct custom_ctx *cctx = (struct custom_ctx*)ctx; cctx->fpath[0] = '/'; cctx->fpath[1] = 's'; cctx->fpath[2] = 'p'; cctx->fpath[3] = 'i'; cctx->fpath[4] = '/'; /* the buf should be the file name */ strcpy(&(cctx->fpath[5]), (const char*)buf);
4)增加msh命令
-
tofile.c默认使用FinSH命令,增加一个msh命令方便使用
//在tofile.c末尾添加 rt_err_t ymodem_rec(uint8_t argc, char **argv) { rt_err_t res; rt_device_t dev = rt_device_find(argv[1]); if (!dev) { rt_kprintf("could not find device:%s\n", argv[1]); return -RT_ERROR; } res = rym_write_to_file(dev); return res; } MSH_CMD_EXPORT(ymodem_rec, receive files by ymodem protocol);
5)使用Ymodem
- 编译并烧录程序,使用SecureCRT连接调试串口
- 在msh命令中输入:ymodem_rec uart1,准备接收文件,在SecureCRT选择Ymodem传输方式,选择文件传输。
三、设备和驱动
RT_Thread设备类型定义
enum rt_device_class_type
{
RT_Device_Class_Char = 0, /**< character device */
RT_Device_Class_Block, /**< block device */
RT_Device_Class_NetIf, /**< net interface */
RT_Device_Class_MTD, /**< memory device */
RT_Device_Class_CAN, /**< CAN device */
RT_Device_Class_RTC, /**< RTC device */
RT_Device_Class_Sound, /**< Sound device */
RT_Device_Class_Graphic, /**< Graphic device */
RT_Device_Class_I2CBUS, /**< I2C bus device */
RT_Device_Class_USBDevice, /**< USB slave device */
RT_Device_Class_USBHost, /**< USB host bus */
RT_Device_Class_SPIBUS, /**< SPI bus device */
RT_Device_Class_SPIDevice, /**< SPI device */
RT_Device_Class_SDIO, /**< SDIO bus device */
RT_Device_Class_PM, /**< PM pseudo device */
RT_Device_Class_Pipe, /**< Pipe device */
RT_Device_Class_Portal, /**< Portal device */
RT_Device_Class_Timer, /**< Timer device */
RT_Device_Class_Miscellaneous, /**< Miscellaneous device */
RT_Device_Class_Sensor, /**< Sensor device */
RT_Device_Class_Unknown /**< unknown device */
};
char *const device_type_str[] =
{
"Character Device",
"Block Device",
"Network Interface",
"MTD Device",
"CAN Device",
"RTC",
"Sound Device",
"Graphic Device",
"I2C Bus",
"USB Slave Device",
"USB Host Bus",
"SPI Bus",
"SPI Device",
"SDIO Bus",
"PM Pseudo Device",
"Pipe",
"Portal Device",
"Timer Device",
"Miscellaneous Device",
"Sensor Device",
"Unknown"
};
rt-thread\bsp\stm32的资源配置在drv_config.h中选择。
rt-thread\bsp\stm32\stm32f429-atk-apollo\board\CubeMX_Config\Src\stm32f4xx_hal_msp.c是芯片具体的资 源配置。
可以通过stm32cubemx生成资源配置文件。
1.Pin设备
应用程序通过 RT-Thread 提供的 PIN 设备管理接口来访问 GPIO,相关接口如下所示:
函数 | 描述 |
---|---|
rt_pin_mode() | 设置引脚模式 |
rt_pin_write() | 设置引脚电平 |
rt_pin_read() | 读取引脚电平 |
rt_pin_attach_irq() | 绑定引脚中断回调函数 |
rt_pin_irq_enable() | 使能引脚中断 |
rt_pin_detach_irq() | 脱离引脚中断回调函数 |
- 这些函数默认没有使用宏定义RTM_EXPORT将其导出到符号表,如果动态模块需要使用,则需要将其导出。
1)获取引脚编号
- 如果使用
rt-thread/bsp/stm32
目录下的 BSP 则可以使用下面的宏获取引脚编号- GET_PIN(port, pin)
- 如果使用其他 BSP 则需要查看 PIN 驱动代码 drv_gpio.c 文件确认引脚编号。此文件里有一个数组存放了每个 PIN 脚对应的编号信息
- 如:STM32F429-apollo是基于STM32F4xx_PIN_NUMBERS == 176的芯片,参考rt-thread\bsp\stm32f429-apollo\drivers\drv_gpio.c的pins[]数组,PB.0和PB.1分别对应index 56和57。
2.ADC
-
开启adc需要把RT_USING_DEVICE_OPS也打开,否则看不到adc设备
- RT-Thread Kernel → Kernel Device Object → Using ops for each device object
-
rt-thread\bsp\stm32f429-apollo的drivers默认没有注册adc设备的驱动,需要自行添加
- 其stm32f4xx_hal_conf.h中也没有define HAL_ADC_MODULE_ENABLED 需要自行打开注释
3.UART
-
bsp\stm32\stm32f429-atk-apollo中只会初始化uart1,需要在底层文件中添加uart2和uart3的初始化程序
- rt-thread\bsp\stm32\stm32f429-atk-apollo\board\CubeMX_Config\Src\stm32f4xx_hal_msp.c是芯片具体的资源配置
- stm32_configure -> HAL_UART_Init -> HAL_UART_MspInit(UART_HandleTypeDef* huart)
void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(huart->Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* Peripheral clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ } if (huart->Instance == USART2) { /* Enable USARTx clock */ __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART2_IRQn, 0, 1); HAL_NVIC_EnableIRQ(USART2_IRQn); } if (huart->Instance == USART3) { /* Enable USARTx clock */ __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART1 GPIO Configuration PB10 ------> USART3_TX PB11 ------> USART3_RX */ GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART3; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART2_IRQn, 0, 1); HAL_NVIC_EnableIRQ(USART2_IRQn); } }