最近在搞一个SD卡的IAP升级,首先弄了个bootloader,再写一个APP,他们必须存储在不同的FLASH地址里,这里我就不讲了,关于IAP升级的方法网上很多,我说下我做这个时遇到的问题
单个的Bootloader 和APP在地址0x800000上运行的时候都是OK的,且Bootloader + app(无uCOSIII系统),运行也是没有问题的;但是但Bootloader + app(有uCOSIII系统)时就卡死了。调试发现卡死分别在两个地方:
1、卡死在printf()函数打印,也初始化了串口函数;
2、卡死在OSStart(&err);进入不了线程;
网上查了很多关于STM32 Bootloader + app(有uCOSIII系统)卡死的问题,大都的方法都是关中断,指定app中断向量表。
void iap_load_app(void)
{
uint8_t i;
__set_PRIMASK(1);
__set_BASEPRI(0);
__set_FAULTMASK(0);
__set_PSP(*(volatile uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS);
__set_MSP(*(volatile uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS);
__set_CONTROL(0);
if(((*(vu32*)USER_FLASH_FIRST_PAGE_ADDRESS)&0x2FFE0000)==0x20000000) //¼ì²éÕ»¶¥µØÖ·ÊÇ·ñºÏ·¨
{
printf("JumpAddress = %x\r\n",*(vu32*)USER_FLASH_FIRST_PAGE_ADDRESS);
jump_addr = *(vu32*)(USER_FLASH_FIRST_PAGE_ADDRESS+4); //Óû§´úÂëÇøµÚ¶þ¸ö×ÖΪ³ÌÐò¿ªÊ¼µØÖ·£¨¸´Î»µØÖ·£©
jump2app = (FunVoidType)jump_addr;
printf("jump_addr = %x\r\n",jump_addr);
USART_ITConfig(USART1, USART_IT_IDLE, DISABLE);
// __disable_irq();
// RCC_DeInit();
// // Disable IRQs
// for( i = 0;i < 8;i++)
// {
// NVIC->ICER[i] = 0xFFFFFFFF;
// }
// // Clear pending IRQs
// for( i = 0;i < 8;i++)
// {
// NVIC->ICPR[i] = 0xFFFFFFFF;
// }
//// __enable_irq();
// SysTick->CTRL =0;
// SysTick->LOAD=0;
// SysTick->VAL=0;
/* Reconfigure vector table offset register to match the application location */
// SCB->VTOR = USER_FLASH_FIRST_PAGE_ADDRESS;
// __set_BASEPRI(0); __set_FAULTMASK(0);
// __set_PSP(*(volatile uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS);
// __set_CONTROL(0);
MSR_MSP(*(volatile uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS); //³õʼ»¯APP¶ÑÕ»Ö¸Õë(Óû§´úÂëÇøµÄµÚÒ»¸ö×ÖÓÃÓÚ´æ·ÅÕ»¶¥µØÖ·)
// __set_PRIMASK(1); //¹ØÖжÏ
// __ISB(); __disable_irq();
jump2app(); //Ìøתµ½APP.
}
}
然而并没什么用,后来比较了一下别人的bsp.c程序,发现这个地方不同,就是系统时钟的获取方法不同
CPU_INT32U BSP_CPU_ClkFreq (void)
{
// CPU_INT32U hclk_freq;
// hclk_freq = SystemCoreClock;这个我自己改的,直接调用系统时钟。
// printf("hclk_freq=%d\r\n",hclk_freq);
// return (hclk_freq);
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks); //»ñÈ¡¸÷¸öʱÖÓƵÂÊ
return ((CPU_INT32U)rcc_clocks.HCLK_Frequency);
}
将原来的注释掉,用这个再测试,居然可以进入线程了,程序也跑起来了,在此之前关闭了所有的printf打印。
hclk_freq = SystemCoreClock;这个我自己改的,直接调用系统时钟,从0x800000上启动是没问题的,但是IAP跳转回来就不行了,估计是跳转回来要重新设置滴答时钟吧,这两者还是有很大区别的;uint32_t SystemCoreClock = 180000000;是系统初始定义的时候就定义好的。并不是真实的系统CPU跑起来的时钟,而 RCC_GetClocksFreq(&rcc_clocks);才是真实频率。
下个问题就是printf()打印的问题了,很多人也说是没有对fputc()函数进行重构,也重构了fputc()函数,而且就是死在
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï
USART1->DR = (u8) ch;
return ch;
}
这个while循环里了;也有的说要在KEIL编译环境里加上use MicroLIB,这个也是勾选的,但就是死在这个while里了
首先这个printf()在app(带系统时),起始地址是0x800000的时候运行时是正常的,没有问题,程序也跑得正常,就是从IAP跳转回来后就死机了
网上说printf()是dos的服务,不可重入,是涉及到硬件部分的,必须改成可重入的函数,如:
OS_CRITICAL_ENTER();
printf("float_num: %.4f\r\n",float_num);
OS_CRITICAL_EXIT();
但还是卡死了。
也不知道具体原因是什么,干脆就用uart的程序打印了
void USART1_Putc(unsigned char c)
{
USART_SendData(USART1, c);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET );
}
void USART1_Puts(char * str)
{
while(*str)
{
USART_SendData(USART1, *str++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}
改为这个打印就没问题了,但是一直用printf()打印,现在换了 ,还真是有点不习惯哈。
还是有些不懂的地方,希望大家能指出来,大家共同学习,分享下。
来源:CSDN
作者:Awen-Linux
链接:https://blog.csdn.net/u010860832/article/details/104431034