华大 MCU 之四 使用问题记录
在最近使用华大 MCU 时,遇到了不少坑,这里记录一下,以方便在以后升级驱动库!其中,有些问题仅仅是在由 ST 切换到 华大之后不太适于的问题,有些是驱动库的更改问题!
如果您发现我说的问题是由于我还没完全了解华大 MCU 造成的,欢迎在评论区指出。亦或是您有更好的见解也欢迎给与指点。
进步始于交流,收获源于分享,不胜感激!
中断向量偏移
在实际项目中,在线升级一般是必须的。在以前 ST 的片子中,IAP + APP 的程序框架是个很不错的选择。该程序框架中,需要配置 APP 的中断向量表偏移(中断向量表偏移寄存器),然而,驱动库并没有给出配置接口。因此,需要自己来实现:
中断弱函数
驱动库提供了一个名为 hc32f46x_interrupts.c/h
的文件,该文件将所有的中断进行了统一的处理,然后以弱函数的形式开发对外接口(hc32f46x_it.c
中定义的函数均为 hc32f46x_interrupts.c/h
中声明的弱函数接口)。但是,驱动库的这种弱函数的使用并不是规范(可以说不正确,导致自己定义的同名函数根本无效)。下面我们以 ST HAL 库最为对比来说明一下。
- 首先,华大驱动库和 ST HAL 库弱函数所使用的的弱函数关键字是有区别的,如下对比图所示:
在使用 GUN 编译器的时候,两者均使用__attribute__((weak))
关键字,然而,在使用 ARM 自家编译器时,华大的驱动库仍然使用__attribute__((weak))
关键字,而 ST HAL 库则会使用 ARM 自家的__weak
关键字。 - 再来看看两者对于弱函数的使用方法,如下对比图所示:
那么,为啥说华大驱动库的这种使用是不正确的呢?这主要是由于 __attribute__((weak))
关键字的原因。使用 __attribute__((weak))
声明的函数,对于所有实现的实体都将被编译器认为为弱函数,不管我们定义多少个接口,都没有用。具体见博文 ARM 之十一__weak 和 attribute((weak)) 关键字的使用。
具体的解决方法有以下两个:
- 修改驱动库改为 ST HAL 库的使用方式。缺点就是需要修改驱动库,不方便后续升级驱动库。
- 如下配置 MDK-ARM 开发环境:
缺点就是,对其他编译器可能并不适用!
中断的使用
在 hc32f46x_interrupts.c/h
的文件中定义的结构体中对中断区分为三大部分,这就导致在我们使用 en_result_t enIrqRegistration(const stc_irq_regi_conf_t *pstcIrqRegiConf)
进行中断注册的时候需要注意分配的中断号。如下图所示:
- 第一部分应该是 Cortex-M 核的中断,他这里处理后,我们就可以自己定义或函数的实体(忽略上面说的问题)来使用中断!
- 第二步部分应该是留给用户自己随便定义的
- 第三部分应该是对于常用的外设,可能为了方便大家使用,他给处理了一层(固定好了中断号)。但是,这里仍然需要我们配置外设中断。当然,我们可以不使用这部分,仍然使用第二部分来自定义。
下面我以我最近使用的 ADC 来看看中断的具体配置。首先,hc32f46x_interrupts.c/h
中对于 ADC 的中断默认分配了中断号 142,如下:
/*********************************************************************************** \brief Int No.142 share IRQ handler********************************************************************************/
void IRQ142_Handler(void)
{uint32_t u32VSSEL142 = M4_INTC->VSSEL142;uint16_t u16Tmp = 0u;/* ADC unit.1 seq. A */if (1ul == bM4_ADC1_ICR_EOCAIEN){if ((1ul == bM4_ADC1_ISR_EOCAF) && (u32VSSEL142 & BIT_MASK_00)){ADC1A_IrqHandler();}}/* ADC unit.1 seq. B */if (1ul == bM4_ADC1_ICR_EOCBIEN){if ((1ul == bM4_ADC1_ISR_EOCBF) && (u32VSSEL142 & BIT_MASK_01)){ADC1B_IrqHandler();}}/* ADC unit.1 seq. A */u16Tmp = M4_ADC1->AWDSR0;if (1ul == bM4_ADC1_AWDCR_AWDIEN){if (((1ul == bM4_ADC1_AWDSR1_AWDF16) || (u16Tmp)) && (u32VSSEL142 & BIT_MASK_02)){ADC1ChCmp_IrqHandler();}}/* ADC unit.1 seq. cmp */if (1ul == bM4_ADC1_AWDCR_AWDIEN){if (((1ul == bM4_ADC1_AWDSR1_AWDF16) || (u16Tmp)) && (u32VSSEL142 & BIT_MASK_03)){ADC1SeqCmp_IrqHandler();}}/* ADC unit.2 seq. A */if (1ul == bM4_ADC2_ICR_EOCAIEN){if ((1ul == bM4_ADC2_ISR_EOCAF) && (u32VSSEL142 & BIT_MASK_04)){ADC2A_IrqHandler();}}/* ADC unit.2 seq. B */if (1ul == bM4_ADC2_ICR_EOCBIEN){if ((1ul == bM4_ADC2_ISR_EOCBF) && (u32VSSEL142 & BIT_MASK_05)){ADC2B_IrqHandler();}}/* ADC unit.2 seq. A */if (1ul == bM4_ADC2_AWDCR_AWDIEN){if ((M4_ADC2->AWDSR0 & 0x1FFu) && (u32VSSEL142 & BIT_MASK_06)){ADC2ChCmp_IrqHandler();}}/* ADC unit.2 seq. cmp */if (1ul == bM4_ADC2_AWDCR_AWDIEN){if ((M4_ADC2->AWDSR0 & 0x1FFu) && (u32VSSEL142 & BIT_MASK_07)){ADC2SeqCmp_IrqHandler();}}
}
其中的 ADC1A_IrqHandler();
等都是放给用户的弱函数接口。我们的使用方法基本就是,给 ADC 配置 142 号中断,然后实现对应的弱函数实体就应该可以了。下面是驱动库的示例给出的代码:
static void AdcIrqConfig(void)
{en_result_t enIrqRegResult;stc_irq_regi_conf_t stcAdcIrqCfg;/* Config ADC1 interrupt. */stcAdcIrqCfg.enIntSrc = INT_ADC1_EOCA;stcAdcIrqCfg.enIRQn = Int117_IRQn;stcAdcIrqCfg.pfnCallback = &ADC1A_IrqHandler;enIrqRegResult = enIrqRegistration(&stcAdcIrqCfg);if (Ok == enIrqRegResult){NVIC_ClearPendingIRQ(stcAdcIrqCfg.enIRQn);NVIC_SetPriority(stcAdcIrqCfg.enIRQn, DDL_IRQ_PRIORITY_03);NVIC_EnableIRQ(stcAdcIrqCfg.enIRQn);}/* Config ADC2 interrupt, use sharing interrupt. */stcAdcIrqCfg.enIntSrc = INT_ADC2_EOCA;stcAdcIrqCfg.enIRQn = Int142_IRQn;enShareIrqEnable(stcAdcIrqCfg.enIntSrc);NVIC_ClearPendingIRQ(stcAdcIrqCfg.enIRQn);NVIC_SetPriority(stcAdcIrqCfg.enIRQn, DDL_IRQ_PRIORITY_04);NVIC_EnableIRQ(stcAdcIrqCfg.enIRQn);
}
注意上面的 enShareIrqEnable(stcAdcIrqCfg.enIntSrc);
要不然会死的很惨哦!从上面的示例可以看出,对于上面说的第三部分的中断使用,需要使用 enShareIrqEnable(stcAdcIrqCfg.enIntSrc);
而不是 enIrqRegistration(&stcAdcIrqCfg);
。
中断反注册
由于我们使用中断(部分)时需要在 hc32f46x_interrupts.c/h
的文件中定义的结构体中进行注册,如果想第二次注册(例如,更改回调接口)时,必须要反注册,否则不能正常注册。这个问题在我们使用 IAP 和 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) /* 如果已经初始化过,这里将不能再初始化 */{stcIntSel->INTSEL = pstcIrqRegiConf->enIntSrc;IrqHandler[pstcIrqRegiConf->enIRQn] = pstcIrqRegiConf->pfnCallback;}else{enRet = ErrorUninitialized;}}return enRet;
}
具体方法就是使用 enIrqResign
函数进行重新标记!
DMA 配置
根据手册,使用 DMA 时需要先写寄存器将 DMA 控制器使能,使能方法是写 DMA 使能寄存器 DMA_EN.EN 位。 如下图所示:
我试了一下,不先使能貌似也没法发现啥问题啊!不知道为啥!?
DMA 无法获取当前传出数据长度
先来说一下需求:在串口驱动中,串口的接收使用 DMA 来实现,DMA 配置为循环模式,在指定缓冲区中循环存放收到的数据,通过读写指针来标记数据的读和写位置。
然而,驱动库中 DMA 接口并没有能获取当前 DMA 传输了多少字节的接口!!!无奈只能选择修改驱动库,添加一些指定的接口,如下图所示:
后来,为了不修改驱动库,我直接把接口放到了自己的驱动里面,然后使用寄存器直接读取:M4DMA1->MONRPT0_f.DRPT
,这样的话,需要注意与通道号的对应关系,如 MONRPT0 即通道 0。
DMA 中断的使用
在华大的 DMA 中,中断默认都是开启的,这点在配置 DMA 的时候需要特殊注意。我们需要使用 DMA 的屏蔽中断寄存器来屏蔽不使用的中断。如下图示:
在实际写代码时,需要调用 en_result_t DMA_DisableIrq(M4_DMA_TypeDef* pstcDmaReg, uint8_t u8Ch, en_dma_irq_sel_t enIrqSel);
来关闭不需要的中中断。例如,DMA 的块传输完成中断 和 传输完成中断 通常不会一起使用!这点对于用惯了 ST MCU 的人来说需要特殊注意!
看门狗
在华大 MCU 中,看门狗也是有两个:SWDT 和 WDT,看手册的介绍应该是分别对应于 ST 的 IWDG 和 WWDG。在实际使用中,多数情况下我们需要使用 SWDT。
目前,SWDT 的配置可以必须在库文件 hc32f46x_icg.h
中进行配置(WDT 可以使用 hc32f46x_wdt.c/h
中进行配置),然后将 hc32f46x_icg.c
包含到自己的项目中,否则配置依旧无效!在更改了驱动库源码之后,在更新驱动库时需要注意!
此外,上面这种配置方法会间接导致一个问题:由于我们的程序分为 IAP 和 APP 两部分。看门狗的配置必须放到 IAP 中,且 APP 中不能再包含该文件,否则在调试烧写时会报错!这是由于 APP 位置的偏移地址导致的!
enumeration value is out of “int” range
在使用 MDK-ARM 编译华大驱动库时,会提示 warning: enumeration value is out of "int" range
,这是由于驱动库中(hc32f46x_exint_nmi_swi.h
、hc32f46x_interrupts.h
)对于枚举类型的值超过了 enum 中的枚举值默认为 int 类型的限制:
我们可以通过以上修改以消除警告!
华大 MCU 之四 使用问题记录相关推荐
- 华大 MCU 之三 时钟控制器(CMU)配置记录
今天在新项目(MCU 为华大 HC32F460)中不再使用外部晶振,转而要使用 HC32F460 内部的 HRC,之前在使用外部晶振时,对华大 MCU 的时钟配置有过一些了解,但是,由于使用内部晶 ...
- 华大 MCU 之一 HC32F460 替换 STM32F411 移植记录
更新 2020年 10 月 21 日,将驱动库更新到了最新版 1.1.1 2020年 10 月 20 日,MCU 由原来的 HC32F460KCTA 更换为 HC32F460KETA 简介 目前, ...
- 华大 MCU 之七 DMA 导致 SPI 异常停止的原因分析、DMA 配置的那些坑
缘起 在最近的项目测试中发现,SPI 通信总是莫名其妙的失败,查看寄存器发现 SPI 已经被停止了.根据手册,SPI 在异常情况下会被强制停止(SPI 的使能为被清零),而根据波形显示通信过程没有 ...
- 华大 MCU 之六 SEGGER Embedded Studio 及 Ozone 使用 Jlink 调试
首先说一下,我之前的开发流程是:VSCode 编辑代码 + Keil 编译及调试.Keil 的调试功能虽然很强大,但是多数功能需要配合 ARM 自家的 ULINKpro 才可以用,例如 Perfo ...
- 华大 MCU 之五 SPI 从机 DMA 模式 配置(不能正常接收问题处理)
最近有个需求是需要使用 华大 MCU(HC32F460) 的 SPI 作为从机来接收数据,无奈搞了两天死活不可用.配置完 SPI 的从机模式后,只要启动主机端的发送就出现如下图所示的错误: 下面是 ...
- l130 华大低功耗mcu_HC32L130国产超低功耗华大MCU芯片介绍
华大HC32L130 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32L130 系列是一款旨在延长便携式测量系统的电池使用寿命的超低功耗.宽电压工作范围的 MCU.集成 12 位 1 ...
- HC32F005 国产低功耗华大MCU芯片介绍
华大HC32F005 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32F005 系列是一款Low Pin Count.宽电压工作范围的MCU.集成12位1M sps 高精度SARAD ...
- HC32L130国产超低功耗华大MCU芯片介绍
华大HC32L130 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32L130 系列是一款旨在延长便携式测量系统的电池使用寿命的超低功耗.宽电压工作范围的 MCU.集成 12 位 1 ...
- HC32L136国产超低功耗华大MCU芯片介绍
华大HC32L136 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32L136 系列是一款旨在延长便携式测量系统的电池使用寿命的超低功耗.宽电压工作范围的 MCU.集成 12 位 1 ...
最新文章
- 引起路由器重启的“元凶”
- llist对象两个属性相乘在相加_Java8使用stream实现list中对象属性的合并(去重并求和)...
- 温故知新MySQL--如何在MySQL表中删除重复行
- Centos7搭建Jira服务器
- Java跨平台实现原理及JVM垃圾回收、内存管理实战
- Aizu - 1407 Parentheses Editor(对顶栈+模拟)
- 3大原则让你的编程之路越走越顺
- java注释类型_Java 8类型注释
- 新代系统plc梯形图说明书_东莞自动化PLC编程需要多少钱
- 【熟能生巧】系列第2期 - 倒计时脚本
- List集合操作一:遍历与查找
- 《BGP设计与实现》一2.11 总结
- 阿里云云计算 36 PolarDB MySQL的管理步骤
- opencv读取视频,读取摄像头
- 基于单片机控制的电动智能小车
- 象棋游戏java代码_象棋游戏 - java代码库 - 云代码
- 2022美赛C题 F奖思路分享
- shader篇-高光反射模型
- laravel与TP的区别
- Android手机界面绘制类似wifi信号图标