華大單片機開發(fā)之HC32F460替換STM32F411移植記錄
- 2020年 10 月 21 日,將驅(qū)動庫更新到了最新版 1.1.1
- 2020年 10 月 20 日,MCU 由原來的 HC32F460KCTA 更換為 HC32F460KETA
簡介
目前,部分產(chǎn)品需要國產(chǎn)化,首當其沖的就是更換國產(chǎn) MCU。在經(jīng)過幾番研究之后,最終決定使用華大的 MCU 來代替(STM32F411 -> HC32F460)。工作的重點就是如何將現(xiàn)有項目代碼一點點移植到 HC32F460 上。以下就是一些在移植過程中的記錄。
首先從官網(wǎng) https://www.hdsc.com.cn/ 下載各種資料、開發(fā)包
開發(fā)環(huán)境
當前所以開發(fā)環(huán)境中,默認都沒有華大的 MCU。因此,需要我們自己從華大的官網(wǎng)下載各 IDE 的配套工具包進行安裝,具體有以下幾個方面:
MCU 選擇
默認 Keil 和 IAR 中并沒有華大的 MCU,必須從其官網(wǎng)下載開發(fā)工具包,然后進行安裝!實際使用中發(fā)現(xiàn),安裝之后的雖然可以選擇華大 MCU,但是,里面竟然沒有我對應(yīng)的 MCU 類型。
貌似,目前其開發(fā)包中僅提供了幾個類型的 MCU。包括下載算法,都沒有我使用的 MCU 的!不過好在我們可以選擇一個資源多一些的 MCU 下載算法代替!
下載算法
安裝了早期的開發(fā)包(1.0.2)后,貌似并沒有對應(yīng)的 FLASH 下載算法,至少 Keil 里面不會顯示下載算法。解決方法是:將華大提供的下載算法 Flash_HC32F46x.FLM
(位于其開發(fā)包中對應(yīng)的開發(fā)環(huán)境目錄下)放到 Keil 的安裝目錄下的 D:\Keil_v5\ARM\Flash
中。不過,最新我從官網(wǎng)下載了最新 1.0.5 版本并安裝,Keil 就可以顯示 FLASH 下載算法了!
注意:如果想更新對應(yīng)的驅(qū)動庫,可要謹慎!我看了一下,新的驅(qū)動庫與舊的并不兼容!
Jlink/J-Flash 的配置
如果開發(fā)中使用 Jlink 進行仿真調(diào)試,那么經(jīng)常會用到 J-Flash 來讀寫 MCU。默認情況下 Jlink 中也是沒有華大 MCU 的。解決方法是,手動編輯 Jlink 的安裝目錄下的配置文件 JLinkDevices.xml
,增加如下內(nèi)容:
<!-- --><!-- HDSC (HC32) --><!-- --><Device><ChipInfoVendor="HDSC"Name="HC32L176"WorkRAMAddr="0x20000000"WorkRAMSize="0x2000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/FlashHC32L17X_128K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L136"WorkRAMAddr="0x20000000"WorkRAMSize="0x2000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_64K"BaseAddr="0x0"MaxSize="0x10000"Loader="Devices/HDSC/FlashHC32L13X_64K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L130"WorkRAMAddr="0x20000000"WorkRAMSize="0x2000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_64K"BaseAddr="0x0"MaxSize="0x10000"Loader="Devices/HDSC/FlashHC32L13X_64K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F030"WorkRAMAddr="0x20000000"WorkRAMSize="0x2000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_64K"BaseAddr="0x0"MaxSize="0x10000"Loader="Devices/HDSC/FlashHC32F030_64K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L110x4"WorkRAMAddr="0x20000000"WorkRAMSize="0x800"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_16K"BaseAddr="0x0"MaxSize="0x4000"Loader="Devices/HDSC/FlashHC32L110_16K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L110x6"WorkRAMAddr="0x20000000"WorkRAMSize="0x1000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_32K"BaseAddr="0x0"MaxSize="0x8000"Loader="Devices/HDSC/FlashHC32L110_32K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F003"WorkRAMAddr="0x20000000"WorkRAMSize="0x800"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_16K"BaseAddr="0x0"MaxSize="0x4000"Loader="Devices/HDSC/FlashHC32F003_16K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F005"WorkRAMAddr="0x20000000"WorkRAMSize="0x1000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_32K"BaseAddr="0x0"MaxSize="0x8000"Loader="Devices/HDSC/FlashHC32F005_32K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L15"WorkRAMAddr="0x20000000"WorkRAMSize="0x1800"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/HC32L15.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F_M14"WorkRAMAddr="0x20000000"WorkRAMSize="0x2000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/HC32F_M14.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F46x"WorkRAMAddr="0x20000000"WorkRAMSize="0x10000"Core="JLINK_CORE_CORTEX_M4"/><FlashBankInfoName="Flash_512K"BaseAddr="0x0"MaxSize="0x80000"Loader="Devices/HDSC/HC32F46x.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L19x"WorkRAMAddr="0x20000000"WorkRAMSize="0x8000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_256K"BaseAddr="0x0"MaxSize="0x40000"Loader="Devices/HDSC/FlashHC32L19X_256K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F19x"WorkRAMAddr="0x20000000"WorkRAMSize="0x8000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_256K"BaseAddr="0x0"MaxSize="0x40000"Loader="Devices/HDSC/FlashHC32F19X_256K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F17x"WorkRAMAddr="0x20000000"WorkRAMSize="0x4000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/FlashHC32F17X_128K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L17x"WorkRAMAddr="0x20000000"WorkRAMSize="0x4000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/FlashHC32L17X_128K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32F072"WorkRAMAddr="0x20000000"WorkRAMSize="0x4000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/FlashHC32F072_128K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device><Device><ChipInfoVendor="HDSC"Name="HC32L07X"WorkRAMAddr="0x20000000"WorkRAMSize="0x4000"Core="JLINK_CORE_CORTEX_M0"/><FlashBankInfoName="Flash_128K"BaseAddr="0x0"MaxSize="0x20000"Loader="Devices/HDSC/FlashHC32L07X_128K.FLM"LoaderType="FLASH_ALGO_TYPE_OPEN"AlwaysPresent="1"/></Device>
注意,如果升級了 Jlink 驅(qū)動,需要重新更改以上內(nèi)容!
驅(qū)動庫移植
這個不是很麻煩,根據(jù)下面的圖,對應(yīng)到原 ST 的目錄結(jié)構(gòu)中即可
啟動引腳
HC32F460 的 BOOT 引腳(PB11)接高電平為從 Flash 啟動,低電平為從 BOOT 啟動。與 ST 正好相反。下面是 ST 和 華大 文檔中的說明:
驅(qū)動庫中斷處理
HC32F460 的驅(qū)動庫提供了一個 hc32f46x_interrupts.c/h
的文件,該文件將所有的中斷進行了統(tǒng)一的處理,然后以弱函數(shù)的形式開發(fā)對外接口(hc32f46x_it.c
中定義的函數(shù)均為 hc32f46x_interrupts.c/h
中聲明的弱函數(shù)接口)。但是,其弱函數(shù)的使用貌似并不是很規(guī)范。在我的項目中,弱函數(shù)并沒有起作用,導(dǎo)致了中斷接口不能用。具體解決方法:
- 修改驅(qū)動庫,具體可以參考 ST 的 HAL 庫中對于弱函數(shù)的使用
- 增加一個連接器參數(shù),如下圖所示:
這么搞,弱函數(shù)的使用上不就復(fù)雜了?多此一舉了?
在實際使用中,大部分中斷(中斷號 32 ~ 127)必須先定義一個回調(diào)函數(shù),然后將使用的中斷注冊到 hc32f46x_interrupts.c/h
中定義的方法表中。例如:
/* Select External Int Ch.3 */ stcIrqRegiConf.enIntSrc = INT_PORT_EIRQ3;/* Register External Int to Vect.No.007 */ stcIrqRegiConf.enIRQn = Int007_IRQn;/* Callback function */ stcIrqRegiConf.pfnCallback =&ExtInt03_Callback;/* 這里定義一個回調(diào)函數(shù) *//* Registration IRQ */enIrqRegistration(&stcIrqRegiConf);
對于不需要注冊的中斷,則直接定義弱函數(shù)接口。例如:
/** ******************************************************************************* ** \brief SysTick interrupt callback function. ** ** \param None ** ** \retval None ** ******************************************************************************/voidSysTick_IrqHandler(void){SysTick_IncTick();}
個人感覺這個文件真的是脫褲子放屁,多此一舉!在啟動的 .s 文件中,各中斷處理函數(shù)接口都已經(jīng)聲明并以死循環(huán)來處理了。這里把中斷再以另一個名字放出去,完全就是為了增加代碼量,在使用上沒有任何便捷之處。這個文件編譯后就近 10K 的代碼量,然后還得占一堆 RAM!有沒有考慮過 MCU 100 多個中斷實際才用幾個,一股腦全給搞一塊去了!
關(guān)于弱函數(shù)的使用,請參考博文 ARM 之十一__weak 和 attribute((weak)) 關(guān)鍵字的使用
由于 FLASH 空間不足,目前正在更換 HC32F460KETA
看門狗
目前,看門狗的配置只能在庫文件 hc32f46x_icg.h
中進行配置,然后將 hc32f46x_icg.c
包含到自己的項目中,否則配置依舊無效!在更改了驅(qū)動庫源碼之后,在更新驅(qū)動庫時需要注意!
此外,上面這種配置方法會間接導(dǎo)致一個問題:由于我們的程序分為 IAP 和 APP 兩部分?撮T狗的配置必須放到 IAP 中,且 APP 中不能再包含該文件,否則在調(diào)試燒寫時會報錯!具體見下文的 [程序下載異常] 章節(jié)
DMA
在使用串口 DMA 接收的時候,發(fā)現(xiàn)驅(qū)動庫提供的接口并不能滿足實際需要。具體表現(xiàn)為,串口的 DMA 是工作在循環(huán)模式下的,這就要求在必要的時候可以獲取 DMA 接收的數(shù)據(jù)長度,不過驅(qū)動庫貌似沒有對應(yīng)的接口!
DMA 驅(qū)動中只有設(shè)置接口,沒有讀取接口! 。無奈自己增加了幾個接口!
GPIO
華大應(yīng)該稱為 PORT
時鐘
貌似,華大 MCU 并不能像 ST MCU 似得,開啟或者關(guān)閉某個 GPIO 的時鐘。所以在配置 IO 時,無需要處理時鐘的問題。
特殊配置
某些 IO 在上電時默認是用作其他功能的。例如:
- PA13,PA14,PA15,PB3,PB4 端口復(fù)位后初始狀態(tài)為 JTAG/SWD 功能有效。
- PC14,PC15 端口復(fù)位后初始狀態(tài)為數(shù)字功能禁止狀態(tài)
- PA11,PA12 與 USBFS_DM,USBFS_DP 引腳復(fù)用,內(nèi)藏約 400KΩ 的下拉電阻,且一直有效。
- 端口 PB11 與 MD 復(fù)用,為輸入專用端口,無輸出功能
這一點貌似和 STM32F1x 的使用情況是一致的。因此,如果需要將上面的 IO 作為普通 IO 使用,則必須進行特殊配置,然后才能進行正常的 IO 配置!
- PA13,PA14,PA15,PB3,PB4 在配置 FSEL[5:0] 選擇功能時需要先將寄存器 PSPCR 相應(yīng)位寫 0 無效JTAG/SWD 功能。對應(yīng)的庫函數(shù)接口
en_result_t PORT_DebugPortSetting(uint8_t u8DebugPort, en_functional_state_t enFunc);
- PC14,PC15 在選擇數(shù)字功能時需要先將相應(yīng)寄存器 PCRxy 的 DDIS 位寫 0 有效數(shù)字功能。這個在進行 IO 配置時,在
en_result_t PORT_Init(en_port_t enPort, uint16_t u16Pin, const stc_port_init_t *pstcPortInit);
中就有對應(yīng)的處理。 - 當系統(tǒng)運行在高速時鐘下,由于 I/O 輸入存在延遲,單周期可能無法正確讀取輸入狀態(tài)值。此時需要設(shè)置寄存器 PCCR.RDWT[1:0],插入若干等待周期。
外設(shè)引腳映射
具體見用戶手冊的 2.2 引腳功能表。在我的項目中,出現(xiàn)了 SPI_CLK 引腳無法映射的問題。
程序下載異常
在使用中發(fā)現(xiàn),在下載程序之后并不能正常啟動。我的項目結(jié)構(gòu)是 IAP(在線升級) + APP,每次下載 APP 之后,IAP 就會被清除。一開始沒有具體分析錯誤的原因的情況下,想當然的通過如下解決方法來處理。
但是,如上圖修改之后,在實際下載的時候,Keil 會提示如下的警告,不過貌似沒啥影響
雖然調(diào)試沒啥影響,但是在生成 .bin 文件時卻會出現(xiàn)錯誤!
但實際該方法并沒有解決根本。通過詳細分析,最后發(fā)現(xiàn)該問題是由于看門狗的配置導(dǎo)致的。具體:由于 APP 中包含了 看門狗的配置,看門狗的配置其實是 FLASH 中的固定一塊空間。我們的 APP 偏移之后,自然沒有看門狗部分的操作算法!
最終解決方法: 我們不需要上面的更改,只需要將看門狗的配置放到 IAP 中即可! IAP 肯定是從起始地址開始,自然不會有問題!
外設(shè)反初始化
我的項目結(jié)構(gòu)是 IAP(在線升級) + APP,外設(shè)在 IAP 中工作正常,但是在 APP 中工作不正常。這主要是由于:外設(shè)在 IAP 中已經(jīng)初始化,然而在調(diào)整到 APP 之后,這部分外設(shè)由于已經(jīng)被初始化,導(dǎo)致不會再次被初始化。這一點其實對于 ST 的 MCU 同樣適用。因此,需要我們來特殊處理!
例如 hc32f46x_interrupts.c/h
中配置的中斷,如果在 IAP 中已經(jīng)被設(shè)置,跳轉(zhuǎn)到 APP 后,就無法再正常配置了(全局變量中會檢查是否被配置過)
en_result_t enIrqRegistration(const stc_irq_regi_conf_t *pstcIrqRegiConf){// todo, assert ... stc_intc_sel_field_t *stcIntSel; en_result_t enRet = Ok;//DDL_ASSERT(NULL != pstcIrqRegiConf->pfnCallback);DDL_ASSERT(IS_NULL_POINT(pstcIrqRegiConf->pfnCallback));/* IRQ032~127 whether out of range */if(((((pstcIrqRegiConf->enIntSrc/32)*6+32)> pstcIrqRegiConf->enIRQn)|| \ (((pstcIrqRegiConf->enIntSrc/32)*6+37)< pstcIrqRegiConf->enIRQn))&& \ (pstcIrqRegiConf->enIRQn >=32)){ enRet = ErrorInvalidParameter;}else{ stcIntSel =(stc_intc_sel_field_t *)((uint32_t)(&M4_INTC->SEL0)+ \ (4u* pstcIrqRegiConf->enIRQn));if(0x1FFu== stcIntSel->INTSEL)/* 如果已經(jīng)初始化過,這里將不能再初始化 */{ stcIntSel->INTSEL = pstcIrqRegiConf->enIntSrc; IrqHandler[pstcIrqRegiConf->enIRQn]= pstcIrqRegiConf->pfnCallback;}else{ enRet = ErrorUninitialized;}}return enRet;}
具體方法就是使用 enIrqResign
函數(shù)進行重新標記!
編輯:admin 最后修改時間:2021-01-18