當前位置:首頁 > 公眾號精選 > 嵌入式云IOT技術圈
[導讀]本程序編寫基于秉火霸道STM32F103ZET6運行環(huán)境。 最近疫情期間,特地將自己大部分硬件資源全部用熱膠搶焊到了一起,以便以后自己復習和學習,當然還有很多,弄不上來了,只能等以后有機會再重新搞一塊!我還是非常舍得花錢買設備的!哈哈!這是一個STM32+Linu














本程序編寫基于秉火霸道STM32F103ZET6運行環(huán)境。

最近疫情期間,特地將自己大部分硬件資源全部用熱膠搶焊到了一起,以便以后自己復習和學習,當然還有很多,弄不上來了,只能等以后有機會再重新搞一塊!我還是非常舍得花錢買設備的!哈哈!這是一個STM32+Linux+51的大雜燴開發(fā)平臺!

1、產生問題

公司的產品,每次生產燒寫程序都得把機器拆開,然后插上串行線或者ST-Link進行燒寫,產品量產的情況下數(shù)量很多,所以生產每次都需要花費很長去時間去給機器燒程序(這里我們用野火的開發(fā)板來模擬)。

2、現(xiàn)有的硬件接口

現(xiàn)在的產品(野火的STM32F103ZET6開發(fā)板)有一個USB接口,硬件連接圖如下:

如上圖所示,當PD3為低電平的時候,USB接口供電,即可用,這一點在上一篇文章已經講解了,我們在STM32CubeMX把這個管腳默認拉低即可。

3、分析問題

STM32CubeMX支持了與USB相關的諸多配置功能,請看如下:

于我們需要使用USB接口來更新程序,所以我們需要在配置USB設備模式的時候給它選擇Download Firmware Update Class(DFU)。

1、USB燒寫原理及流程分析

1.1 燒寫原理

這點與IAP升級是大同小異的,只不過這里我們使用了USB來燒寫,之前寫過類似的一篇文章:帶串口屏顯示的BootLoader程序開發(fā) 在這篇文章里面也介紹了相應的原理,這里就不再重復描述,我們負責把這篇文章里提到的幾點實現(xiàn)就可以了。

1.2 程序存儲分區(qū)

STM32F103ZET6的FLASH容量一共有512KB。所以,我給BootLoader的大小是64K,也就是0x10000,具體是怎么算的呢?

0x10000轉十進制為65536,65536/1024 = 64K

把剩下的空間全部分配給APP,也就是0x70000,具體是怎么算的呢?

0x70000轉十進制為458752,458752/1024 = 448K

4、解決問題

4.1 配置編寫B(tài)ootLoader程序的CubeMX工程

4.1.1 配置RCC時鐘

4.1.2 配置串行調試接口

4.1.3 配置按鍵、調試燈、調試串口、USB使能管腳

調試燈選擇的是PB1,低電平點亮,具體可以看原理圖:

USB使能管腳默認為低電平。

選用USART2作為調試打印輸出。

4.1.4 配置USB相關的選項

配置的基本參數(shù)默認即可,不需要改變。

在中斷設置這里,將USB優(yōu)先級調低,可以避免一些默認其妙不穩(wěn)定的現(xiàn)象。接下來配置USB設備相關的選項。

類參數(shù)有一個字段比較重要:

@Internal Flash   /0x08000000/03*016Ka,01*016Kg,01*064Kg,07*128Kg,04*016Kg,01*064Kg,07*128Kg

這個參數(shù)的具體含義描述如下:

  • @:檢測到這是一個特殊的映射描述符(避免解碼標準描述符)

  • /:用于區(qū)域之間的分隔符

  • 每個地址以“ 0x”開頭的最大8位數(shù)字

  • /:用于區(qū)域之間的分隔符

  • 扇區(qū)數(shù)的最大2位數(shù)字

  • *:用于扇區(qū)數(shù)和扇區(qū)大小之間的分隔符

  • 扇區(qū)大小在0到999之間的最大3位

  • 扇區(qū)大小乘數(shù)的1位數(shù)字。有效條目為:B(字節(jié)),K(千),M(兆)

  • 扇區(qū)類型的1位數(shù)字,如下所示:

– a(0x41):可讀
– b(0x42):可擦除
– c(0x43):可讀和可擦除
(0x44):可寫
– e(0x45):可讀寫
–f(0x46):可擦除和可寫
–g(0x47):可讀寫,可寫

4.1.5 生成工程

這里默認不讓它自動生成main函數(shù),main函數(shù)我們自己寫。在配置USB設備參數(shù)里,USBD_DFU_XFER_SIZE參數(shù):USB數(shù)據pack大小,越大配置速度越快。默認配置1024Bytes. 1024Bytes使用的是堆空間,故堆空間要大于1024Bytes. 原因:代碼如下。

#define USBD_malloc         malloc
/* Allocate Audio structure */
pdev->pClassData = USBD_malloc(sizeof (USBD_DFU_HandleTypeDef));

所以這里的堆我把它配置成0x1000。(個人習慣)

4.2 編寫B(tài)ootLoader程序

4.2.1 實現(xiàn)usbd_dfu_if.c中相關的接口

宏定義一些參數(shù)

//FLASH的擦寫實現(xiàn)
#define FLASH_ERASE_TIME (uint16_t)50
#define FLASH_PROGRAM_TIME (uint16_t)50
//APP存放的結束地址
#define USBD_DFU_APP_END_ADD 0x08080000
//FLASH頁大小
#define FLASH_PAGE_SIZE 0x800U //2K

實現(xiàn)如下接口:

MEM_If_Init_FS,       閃存初始化,解鎖內部flash。
MEM_If_DeInit_FS, 閃存反(取消)初始化,上鎖內部flash。
MEM_If_Erase_FS, 閃存擦除。
MEM_If_Write_FS, 閃存寫入。
MEM_If_Read_FS, 閃存讀取。
MEM_If_GetStatus_FS 獲取閃存狀態(tài),返回寫入或擦除操作所需的時間。

閃存初始化,解鎖內部flash。

uint16_t MEM_If_Init_FS(void)
{
/* USER CODE BEGIN 0 */
//解鎖內部FLASH
HAL_FLASH_Unlock();
//清除FLASH的一些標志,可以避免一些莫名其妙的問題
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR);
return (USBD_OK);
/* USER CODE END 0 */
}

閃存反(取消)初始化,上鎖內部flash。

uint16_t MEM_If_DeInit_FS(void)
{
/* USER CODE BEGIN 1 */
//給FLASH上鎖
HAL_FLASH_Lock();
return (USBD_OK);
/* USER CODE END 1 */
}

閃存擦除。

uint16_t MEM_If_Erase_FS(uint32_t Add)
{
/* USER CODE BEGIN 2 */
/*擦除整個APP程序存放的空間,即是0x08080000-0x08010000*/
/*
因為起始地址是0x8000000,而Size是0x80000,所以MCU存放代碼的最后一個區(qū)域的地址為0x8080000。
而DFU占了其中的0x10000的空間。
*/
uint32_t NbOfPages = 0 ;
uint32_t PageError = 0 ;
FLASH_EraseInitTypeDef pEraseInit ;
NbOfPages = (USBD_DFU_APP_END_ADD - USBD_DFU_APP_DEFAULT_ADD)/FLASH_PAGE_SIZE ;
pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
pEraseInit.PageAddress = USBD_DFU_APP_DEFAULT_ADD;
pEraseInit.NbPages = NbOfPages; //erase all pages of APP
if(HAL_FLASHEx_Erase(&pEraseInit,&PageError)!= HAL_OK)
return USBD_FAIL ;
return (USBD_OK);
/* USER CODE END 2 */
}

閃存寫入。

uint16_t MEM_If_Write_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
/* USER CODE BEGIN 3 */
uint32_t i =0;

for(i=0;i<Len;i+=4)
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,(uint32_t)(dest+i),*(uint32_t*)(src+i))== HAL_OK)
{
if(*(uint32_t*)(src+i) != *(uint32_t*)(dest+i))
return USBD_FAIL;
}
else
{
return USBD_FAIL;
}
}
return (USBD_OK);
/* USER CODE END 3 */
}

閃存讀取

uint8_t *MEM_If_Read_FS(uint8_t *src, uint8_t *dest, uint32_t Len)
{
/* Return a valid address to avoid HardFault */
/* USER CODE BEGIN 4 */
uint32_t i = 0;
uint8_t *psrc = src;

for (i = 0; i < Len; i++)
{
dest[i] = *psrc++;
}

return (uint8_t*) (dest);
/* USER CODE END 4 */
}

獲取閃存狀態(tài),返回寫入或擦除操作所需的時間。

uint16_t MEM_If_GetStatus_FS(uint32_t Add, uint8_t Cmd, uint8_t *buffer)
{
/* USER CODE BEGIN 5 */
switch (Cmd)
{
case DFU_MEDIA_PROGRAM:
buffer[1] = (uint8_t)FLASH_PROGRAM_TIME;
buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8);
buffer[3] = 0;
break;

case DFU_MEDIA_ERASE:
buffer[1] = (uint8_t)FLASH_ERASE_TIME;
buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);
buffer[3] = 0;
break ;
default:

break;
}

return (USBD_OK);
/* USER CODE END 5 */
}

4.2.1 實現(xiàn)main.c

定義調試打印接口,這里我用的是USART2

int fputc(int ch, FILE* FILE)
{
HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
return ch;
}

跳轉到APP的代碼實現(xiàn):

static void JumpToApp(void)
{
typedef void (*pFunction)(void);
static pFunction JumpToApplication;
static uint32_t JumpAddress;

/* Test if user code is programmed starting from USBD_DFU_APP_DEFAULT_ADD * address */
if (((*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t *) (USBD_DFU_APP_DEFAULT_ADD + 4);
JumpToApplication = (pFunction) JumpAddress;

/* Initialize user application's Stack Pointer */
__set_MSP((*(__IO uint32_t *) USBD_DFU_APP_DEFAULT_ADD));
JumpToApplication();
}
}

在正常啟動過程中,如果APP區(qū)域存放有數(shù)據,我們不希望去啟動USB,在剛開始的時候我們可以把USB的功能給失能掉,如果檢測到APP區(qū)域沒有數(shù)據,則再初始化USB功能,所以在這里編寫一個USB的失能函數(shù)。

static void USB_GPIO_DeInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();

/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);

/*Configure GPIO pin*/
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_Delay(500);
}

main函數(shù)實現(xiàn)

int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
USB_GPIO_DeInit();
MX_USART2_UART_Init();
/*如果沒有按下按鍵,則自動跳轉到APP區(qū),如果跳轉不過去,則代表區(qū)域無APP*/
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) != GPIO_PIN_SET)
{
JumpToApp();
printf("跳轉失敗,開始進入DFU模式\r\n");
}
//進入DFU模式
MX_USB_DEVICE_Init();
printf("Bruce.Yang DFU\n");
//調試燈常亮,代表此時在DFU模式
HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin,GPIO_PIN_RESET);
while(1)
{
HAL_Delay(1000);
}
}

實現(xiàn)完畢,接下來可以編譯程序,下載到開發(fā)板,由于沒有APP,所以開發(fā)板上PB1的燈常亮。

4.2.2 編寫APP程序

APP程序很簡單,就讓PB1燈以500ms的頻率進行翻轉吧。

配置過程(略)太簡單了,應用APP的核心代碼如下:

 while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_TogglePin(BLUE_LED_GPIO_Port,BLUE_LED_Pin);
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}

接下來主要是在工程里做一些設置。

1、點擊魔術棒設置APP啟動的地址

2、更改中斷向量表偏移

接下來編譯生成APP_TEST.hex文件,我們用一個工具來將它燒寫到板子上。

安裝DFU燒錄軟件:DfuSe_Demo

官網下載鏈接:

https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-programmers/stsw-stm32080.html#resource

默認安裝即可。

安裝成功后得到兩個軟件。


Dfu file manager是把bin文件或者hex文件生成 .dfu后綴的文件, .dfu后綴的文件就是我們的固件。DfuSe_Demo是燒錄 文件后綴 .dfu 軟件。

燒錄步驟:

1、將.hex文件轉化成.dfu后綴的文件

生成后可以看到效果:

2、連接USB到開發(fā)板的設備端口到PC

看到沒有識別DFU

我們需要手動給它更新下驅動程序,直接就是剛剛下載的DfuSe安裝的目錄下找對應系統(tǒng)版本的驅動就好了。

最后可以看到該模式被識別了:

接下來打開DfuSeDemo這個軟件,可以看到開發(fā)板現(xiàn)在已經被識別了。

接下來將剛剛生成的APP_TEST1.dfu加載進來。

點擊Upgrade進行升級。

升級成功!接下來點擊Leave DFU mode,程序則會自動開始執(zhí)行。


這時候APP已經跑起來了,燈在以500ms的頻率不斷閃爍。

至此USB DFU固件成功!

Bootloader代碼以及APP代碼在這里下載:

鏈接:https://pan.baidu.com/s/1zRv7j4E8SXgCV5F6RbSo1Q
提取碼:5539

如果有興趣的話,還可以把我之前寫的串口屏BootLoader那個程序繼續(xù)升級一下!

帶串口屏顯示的Bootloader

往期精彩


帶串口屏顯示的Bootloader

為Linux應用構造有限狀態(tài)機

編程修養(yǎng)(精品文,建議認真品讀并實踐)

嵌入式C語言代碼優(yōu)化方案(深度好文,建議花時間研讀并收藏)

若覺得本次分享的文章對您有幫助,隨手點[在看]并轉發(fā)分享,也是對我的支持。

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或將催生出更大的獨角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據媒體報道,騰訊和網易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據產業(yè)博覽會開幕式在貴陽舉行,華為董事、質量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據產業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經營業(yè)績穩(wěn)中有升 落實提質增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉