最近在搞一個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()打印,現在換了 ,還真是有點不習慣哈。
還是有些不懂的地方,希望大家能指出來,大家共同學習,分享下。