STM32 CubeMX (uart_IAP串口)简单示例

STM32 CubeMX


STM32 CubeMX (串口IAP)

  • STM32 CubeMX
    • IAP有什么用?
    • 整体思路
  • 一、STM32 CubeMX 设置
    • 时钟树
    • UART使能
    • UART初始化设置
  • 二、代码部分
    • 文件移植![在这里插入图片描述](https://img-blog.csdnimg.cn/0c4841d8328b4169a8833f15fe3d670c.png)
    • 实验效果
    • APP程序返回IAP程序


IAP有什么用?

Iap,全名为in applacation programming,即在应用编程,与之相对应的叫做isp,in system programming,在系统编程,两者的不同是isp需要依靠烧写器在单片机复位离线的情况下编程,需要人工的干预,而iap则是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。在工程应用中经常会出现我们的产品被安装在某个特定的机械结构中,更新程序的时候拆机很不方便,使用iap技术能很好地降低工作量.

整体思路

实现iap有两个很重要的前提,首先,单片机程序能对自身的内部flash进行擦写,第二,单片机要有能够和外部进行通讯的方式,无论是网络还是别的方式,只要能传输数据就行

要做iap首先我们要知道stm32的启动流程,流程如下

  1. 单片机从0x80000000位置启动,并将该地址当成系统栈顶地址
  2. 运行到中断向量表中,默认的中断向量表为0x80000004,该位置存放复位中断
  3. 跳转到复位中断处理函数当中,进行系统初始化,然后运行main函数

两种实现方式:

  1. 将IAP和APP程序放在一个程序中,会增加代码的耦合性和难度;
  2. 要将单片机flash分为两部份 IAP程序 APP程序 ;IAP是烧录工具烧录的(如 JTAG 或 ISP 烧入),App是通过串口传递bin文件(也可以是其他方式:如蓝牙,网卡,CAN等等)然后芯片自己烧录的

一定要划分清楚IAP程序和APP程序的flash地址,不要重合了
#1.IAP程序设置,程序地址
STM32 CubeMX (uart_IAP串口)简单示例_第1张图片
#2.APP程序设置,程序地址
STM32 CubeMX (uart_IAP串口)简单示例_第2张图片
生成bin文件

fromelf.exe --bin -o "[email protected]" "#L

STM32 CubeMX (uart_IAP串口)简单示例_第3张图片

一、STM32 CubeMX 设置

时钟树

STM32 CubeMX (uart_IAP串口)简单示例_第4张图片

UART使能

STM32 CubeMX (uart_IAP串口)简单示例_第5张图片

UART初始化设置

STM32 CubeMX (uart_IAP串口)简单示例_第6张图片

二、代码部分

移植正点原子的两部分代码,IAP和Fashl,也可以移植官方的代码,有能实现的Dome都可以

文件移植STM32 CubeMX (uart_IAP串口)简单示例_第7张图片

STM32 CubeMX (uart_IAP串口)简单示例_第8张图片

移植至CubeMX生成的文件夹
STM32 CubeMX (uart_IAP串口)简单示例_第9张图片
添加文件和路径
STM32 CubeMX (uart_IAP串口)简单示例_第10张图片

修改IAP.C

#define FLASH_APP1_ADDR		0x8005000  	//第一个应用程序起始地址(存放在FLASH)
iapfun jump2app;
u16 iapbuf[512];//**缓存区大小,因为stm32f103c8t6的flash一页是1K的,所以要改小为512**

添加函数


//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

修改"stmflash.h"

//用户根据自己的需要设置
#define STM32_FLASH_SIZE 	64	 		//所选STM32的FLASH容量大小(单位为K)
#define STM32_FLASH_WREN 	1              	//使能FLASH写入(0,不是能;1,使能)
#define FLASH_WAITETIME  	50000          	//FLASH等待超时时间

//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 		//STM32 FLASH的起始地址

修改UART

#include "usart.h"
#include "stdio.h"
int fputc(int ch, FILE *f) {
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}

/* USER CODE BEGIN 0 */
#define USART_REC_LEN  			15*1024 //定义最大接收字节数 55K
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
#define RXBUFFERSIZE   1 					//缓存大小	  	
uint8_t USART_RX_BUF[USART_REC_LEN] __attribute__ ( ( at ( 0X20001000 ) ) ); //接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
uint16_t USART_RX_STA = 0;       	//接收状态标记
uint16_t USART_RX_CNT = 0;			//接收的字节数
/* USER CODE END 0 */
u8 aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲

UART_HandleTypeDef 	huart1;

/* USART1 init function */

//初始化IO 串口1 
//bound:波特率
void uart_init(u32 bound)
{	
	//UART 初始化设置
	huart1.Instance=USART1;					    //USART1
	huart1.Init.BaudRate=bound;				    //波特率
	huart1.Init.WordLength=UART_WORDLENGTH_8B;   //字长为8位数据格式
	huart1.Init.StopBits=UART_STOPBITS_1;	    //一个停止位
	huart1.Init.Parity=UART_PARITY_NONE;		    //无奇偶校验位
	huart1.Init.HwFlowCtl=UART_HWCONTROL_NONE;   //无硬件流控
	huart1.Init.Mode=UART_MODE_TX_RX;		    //收发模式
	HAL_UART_Init(&huart1);					    //HAL_UART_Init()会使能UART1
	
	HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
  
}

//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_Initure;
	
	if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
		__HAL_RCC_AFIO_CLK_ENABLE();
	
		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		GPIO_Initure.Mode=GPIO_MODE_AF_INPUT;	//模式要设置为复用输入模式!	
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
		
#if EN_USART1_RX
		HAL_NVIC_EnableIRQ(USART1_IRQn);				//使能USART1中断通道
		HAL_NVIC_SetPriority(USART1_IRQn,3,3);			//抢占优先级3,子优先级3
#endif	
	}
}
extern u8 flag_1;

//串口1中断服务程序
void USART1_IRQHandler(void)                	
{ 
	u8 Res;
	if((__HAL_UART_GET_FLAG(&	huart1,UART_FLAG_RXNE)!=RESET))  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res=USART1->DR; 
		if(USART_RX_CNT<USART_REC_LEN)
		{
			USART_RX_BUF[USART_RX_CNT]=Res;
			USART_RX_CNT++;			 									     
		}
	}
	  flag_1=1;
	HAL_UART_IRQHandler(&	huart1);	
} 

main.c

int main(void)
{
  /* USER CODE BEGIN 1 */
	  u16 oldcount = 0;				//老的串口接收数据值
		u16 applenth = 0;				//接收到的app代码长度
		u16 app_bin = 0;
		u16 app_enter = 0;
 
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  uart_init(115200);
   while ( 1 )
    {
		//	 首先判断app代码的首地址是否为0x0800 000,是则进入app,否的话进行引导区。
        if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
        {
            iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码
        }
		//   判断app代码栈顶是否为0x2000 000,不是则进入升级模式,代码如下,其中 FLASH_APP1_ADDR=0x8005000;
        if ( ( ( ( * ( vu32* ) FLASH_APP1_ADDR ) & 0x2FFE0000 ) != 0x20000000 ) )
        {
            printf ( "/***** No APP! *****/ \r\n" );
            printf ( "stm32f103c8t6在线升级  \r\n" );
            printf ( "选择对应的app bin文件 \r\n" );
            printf ( "输入 A 发送bin文件 \r\n" );
            printf ( "输入 E 进入app \r\n" );
            while ( 1 )
            {
							 printf ( "滴答!\r\n" );
							if( flag_1==1)
							{
							   flag_1=0;
						  	 printf ( "holle wored!\r\n" );
							}
                if ( USART_RX_CNT )
                {
                    if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成.
                    {
                        applenth = USART_RX_CNT;
                        oldcount = 0;
                        USART_RX_CNT = 0;
                        if ( applenth > 100 )
                        {
                            printf ( "用户程序接收完成!\r\n" );
                            printf ( "代码长度:%dBytes\r\n", applenth );
                        }

                    }
                    else 
                        oldcount = USART_RX_CNT;
                }
                HAL_Delay(1000);
								
                if ( USART_RX_BUF[0] == 'A' )
                {
                    if ( applenth )
                        printf ( "\r\n 请发送bin文件 \r\n" );
                    app_bin = 1;
                    applenth = 0;
                }
                else if ( app_bin )
                {

                    if ( applenth )
                    {
                        printf ( "开始更新固件...\r\n" );
                        printf ( "Copying APP2FLASH..." );
						//此处 0X20001000 地址为串口缓冲区开始接收数据地址
                        if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. 串口是否接收到数据
                        {
                            
                            iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码 
                            printf ( "Copy APP Successed!!" );
                            printf ( "固件更新完成!\r\n" );
                            applenth = 0;
                            app_bin = 0;
                        }
                    }
                }

                if ( USART_RX_BUF[0] == 'E' )
                {
                    if ( applenth )
                        printf ( "\r\n 将要执行APP \r\n" );
                    app_enter = 1;
                    applenth = 0;
                } 
                if ( app_enter )
                {
                    printf ( "开始执行FLASH用户代码!!\r\n" );
                    if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
                    {
                        
                        iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码
                    }
                    else 
                        {
                            printf ( "非FLASH应用程序,无法执行!\r\n" );
                            printf ( "Illegal FLASH APP!" );
                            
                        }
                    }
            }
        }

    }
  /* USER CODE END 3 */
}

实验效果

STM32 CubeMX (uart_IAP串口)简单示例_第11张图片

APP程序返回IAP程序

执行下面代码可以重回函数

 			  SCB->VTOR = FLASH_BASE | 0x0000;
			  HAL_NVIC_SystemReset();

你可能感兴趣的:(STM32,CubeMX,stm32,嵌入式硬件,单片机)