在最近使用华大 MCU 时,遇到了不少坑,这里记录一下,以方便在以后升级驱动库!其中,有些问题仅仅是在由 ST 切换到 华大之后不太适于的问题,有些是驱动库的更改问题!

  如果您发现我说的问题是由于我还没完全了解华大 MCU 造成的,欢迎在评论区指出。亦或是您有更好的见解也欢迎给与指点。

进步始于交流,收获源于分享,不胜感激!

中断向量偏移

  在实际项目中,在线升级一般是必须的。在以前 ST 的片子中,IAP + APP 的程序框架是个很不错的选择。该程序框架中,需要配置 APP 的中断向量表偏移(中断向量表偏移寄存器),然而,驱动库并没有给出配置接口。因此,需要自己来实现:

中断弱函数

  驱动库提供了一个名为 hc32f46x_interrupts.c/h 的文件,该文件将所有的中断进行了统一的处理,然后以弱函数的形式开发对外接口(hc32f46x_it.c 中定义的函数均为 hc32f46x_interrupts.c/h 中声明的弱函数接口)。但是,驱动库的这种弱函数的使用并不是规范(可以说不正确,导致自己定义的同名函数根本无效)。下面我们以 ST HAL 库最为对比来说明一下。

  1. 首先,华大驱动库和 ST HAL 库弱函数所使用的的弱函数关键字是有区别的,如下对比图所示:

      在使用 GUN 编译器的时候,两者均使用 __attribute__((weak)) 关键字,然而,在使用 ARM 自家编译器时,华大的驱动库仍然使用 __attribute__((weak)) 关键字,而 ST HAL 库则会使用 ARM 自家的 __weak 关键字。
  2. 再来看看两者对于弱函数的使用方法,如下对比图所示:

  那么,为啥说华大驱动库的这种使用是不正确的呢?这主要是由于 __attribute__((weak)) 关键字的原因。使用 __attribute__((weak)) 声明的函数,对于所有实现的实体都将被编译器认为为弱函数,不管我们定义多少个接口,都没有用。具体见博文 ARM 之十一__weak 和 attribute((weak)) 关键字的使用。

具体的解决方法有以下两个:

  1. 修改驱动库改为 ST HAL 库的使用方式。缺点就是需要修改驱动库,不方便后续升级驱动库。
  2. 如下配置 MDK-ARM 开发环境:

    缺点就是,对其他编译器可能并不适用!

中断的使用

  在 hc32f46x_interrupts.c/h 的文件中定义的结构体中对中断区分为三大部分,这就导致在我们使用 en_result_t enIrqRegistration(const stc_irq_regi_conf_t *pstcIrqRegiConf) 进行中断注册的时候需要注意分配的中断号。如下图所示:

  1. 第一部分应该是 Cortex-M 核的中断,他这里处理后,我们就可以自己定义或函数的实体(忽略上面说的问题)来使用中断!
  2. 第二步部分应该是留给用户自己随便定义的
  3. 第三部分应该是对于常用的外设,可能为了方便大家使用,他给处理了一层(固定好了中断号)。但是,这里仍然需要我们配置外设中断。当然,我们可以不使用这部分,仍然使用第二部分来自定义。

  下面我以我最近使用的 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.hhc32f46x_interrupts.h)对于枚举类型的值超过了 enum 中的枚举值默认为 int 类型的限制:

我们可以通过以上修改以消除警告!

华大 MCU 之四 使用问题记录相关推荐

  1. 华大 MCU 之三 时钟控制器(CMU)配置记录

      今天在新项目(MCU 为华大 HC32F460)中不再使用外部晶振,转而要使用 HC32F460 内部的 HRC,之前在使用外部晶振时,对华大 MCU 的时钟配置有过一些了解,但是,由于使用内部晶 ...

  2. 华大 MCU 之一 HC32F460 替换 STM32F411 移植记录

    更新 2020年 10 月 21 日,将驱动库更新到了最新版 1.1.1 2020年 10 月 20 日,MCU 由原来的 HC32F460KCTA 更换为 HC32F460KETA 简介   目前, ...

  3. 华大 MCU 之七 DMA 导致 SPI 异常停止的原因分析、DMA 配置的那些坑

    缘起   在最近的项目测试中发现,SPI 通信总是莫名其妙的失败,查看寄存器发现 SPI 已经被停止了.根据手册,SPI 在异常情况下会被强制停止(SPI 的使能为被清零),而根据波形显示通信过程没有 ...

  4. 华大 MCU 之六 SEGGER Embedded Studio 及 Ozone 使用 Jlink 调试

      首先说一下,我之前的开发流程是:VSCode 编辑代码 + Keil 编译及调试.Keil 的调试功能虽然很强大,但是多数功能需要配合 ARM 自家的 ULINKpro 才可以用,例如 Perfo ...

  5. 华大 MCU 之五 SPI 从机 DMA 模式 配置(不能正常接收问题处理)

      最近有个需求是需要使用 华大 MCU(HC32F460) 的 SPI 作为从机来接收数据,无奈搞了两天死活不可用.配置完 SPI 的从机模式后,只要启动主机端的发送就出现如下图所示的错误: 下面是 ...

  6. l130 华大低功耗mcu_HC32L130国产超低功耗华大MCU芯片介绍

    华大HC32L130 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32L130 系列是一款旨在延长便携式测量系统的电池使用寿命的超低功耗.宽电压工作范围的 MCU.集成 12 位 1 ...

  7. HC32F005 国产低功耗华大MCU芯片介绍

    华大HC32F005 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32F005 系列是一款Low Pin Count.宽电压工作范围的MCU.集成12位1M sps 高精度SARAD ...

  8. HC32L130国产超低功耗华大MCU芯片介绍

    华大HC32L130 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32L130 系列是一款旨在延长便携式测量系统的电池使用寿命的超低功耗.宽电压工作范围的 MCU.集成 12 位 1 ...

  9. HC32L136国产超低功耗华大MCU芯片介绍

    华大HC32L136 系列32 位 ARM® Cortex®-M0+ 微控制器 HC32L136 系列是一款旨在延长便携式测量系统的电池使用寿命的超低功耗.宽电压工作范围的 MCU.集成 12 位 1 ...

最新文章

  1. 引起路由器重启的“元凶”
  2. llist对象两个属性相乘在相加_Java8使用stream实现list中对象属性的合并(去重并求和)...
  3. 温故知新MySQL--如何在MySQL表中删除重复行
  4. Centos7搭建Jira服务器
  5. Java跨平台实现原理及JVM垃圾回收、内存管理实战
  6. Aizu - 1407 Parentheses Editor(对顶栈+模拟)
  7. 3大原则让你的编程之路越走越顺
  8. java注释类型_Java 8类型注释
  9. 新代系统plc梯形图说明书_东莞自动化PLC编程需要多少钱
  10. 【熟能生巧】系列第2期 - 倒计时脚本
  11. List集合操作一:遍历与查找
  12. 《BGP设计与实现》一2.11 总结
  13. 阿里云云计算 36 PolarDB MySQL的管理步骤
  14. opencv读取视频,读取摄像头
  15. 基于单片机控制的电动智能小车
  16. 象棋游戏java代码_象棋游戏 - java代码库 - 云代码
  17. 2022美赛C题 F奖思路分享
  18. shader篇-高光反射模型
  19. laravel与TP的区别
  20. Android手机界面绘制类似wifi信号图标

热门文章

  1. 安装Orcale 10g SP3时报oui.exe异常错误
  2. Queue 队列的用法
  3. NOIp 2014 #2 联合权值 Label:图论 !!!未AC
  4. OpenCV学习(1) OpenCV的安装
  5. JavaScript规范和工具 JSlint
  6. pandas(三) -- DataFrame的基本操作
  7. leetcode 遇到的问题
  8. 让产品经理GC的文本段落两端对齐css
  9. 【GVA】gin gorm多对多many2many更新数据时级联更新关联表数据的正确写法
  10. 通过实战跑分来展示HBase2.x的写入性能