华大HC32F005 Systick问题

在项目中使用了HC32F005这颗IC,遇到一个新版本DDL库的问题,现象是使能了BGR模块后,系统systick中断停止了。

int Adc_init(void)
{
    if (Ok != Sysctrl_SetPeripheralGate(SysctrlPeripheralAdcBgr, TRUE)) // 内建电压模块时钟使能
    {
        return Error;
    }       
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);    //GPIO 外设时钟使能
    Gpio_SetAnalogMode(BATT_ADC_PORT, BATT_ADC_PIN);
	
    Adc_Enable();
    Bgr_BgrEnable();
    
    stc_adc_cfg_t      stcAdcCfg;
    stc_adc_norm_cfg_t stcAdcNormCfg;

    DDL_ZERO_STRUCT(stcAdcCfg);
    DDL_ZERO_STRUCT(stcAdcNormCfg);

    stcAdcCfg.enAdcOpMode = AdcNormalMode;          //单次采样模式
    stcAdcCfg.enAdcClkSel = AdcClkSysTDiv1;         //PCLK
    stcAdcCfg.enAdcSampTimeSel = AdcSampTime4Clk;   //4个采样时钟
    stcAdcCfg.enAdcRefVolSel = RefVolSelInBgr2p5;   //参考电压:内部2.5V(avdd>3V,SPS<=200kHz)  SPS速率 = ADC时钟 / (采样时钟 + 16CLK) 
    stcAdcCfg.bAdcInBufEn = FALSE;                  //电压跟随器如果使能,SPS采样速率 <=200K
    stcAdcCfg.u32AdcRegHighThd = 0u;                //比较阈值上门限
    stcAdcCfg.u32AdcRegLowThd = 0u;                 //比较阈值下门限
    stcAdcCfg.enAdcTrig0Sel = AdcTrigDisable;       //ADC转换自动触发设置
    stcAdcCfg.enAdcTrig1Sel = AdcTrigDisable;
    Adc_Init(&stcAdcCfg);    

    stcAdcNormCfg.enAdcNormModeCh = AdcExInputCH6;  //通道6 P36
    stcAdcNormCfg.bAdcResultAccEn = FALSE;
    Adc_ConfigNormMode(&stcAdcCfg, &stcAdcNormCfg);

    return 0;
}

通过排除法,定位到了关键代码:

    Bgr_BgrEnable();

由于使用了新版本的DDL库(V1.9.0),对BGR模块的操作封装成了库函数 Bgr_BgrEnable(),代替旧版本(V1.8.0)直接操作寄存器的方式

    M0P_BGR->CR_f.BGR_EN = 0x1u;	//使能内建电压模块
    M0P_BGR->CR_f.TS_EN = 0x0u;

而 Bgr_BgrEnable的实现如下:

void Bgr_BgrEnable(void)
{    
    M0P_BGR->CR |= 0x1u;
    
    delay10us(2);
}

其中M0P_BGR的CR与CR_f成员是联合类型,其中CR_f又使用了位域的方式定义了几个寄存器:

typedef struct
{
    __IO uint32_t BGR_EN                    : 1;
    __IO uint32_t TS_EN                     : 1;
    uint32_t RESERVED2                      :29;
    __IO uint32_t RSV                       : 1;
} stc_bgr_cr_field_t;
typedef struct
{
    union
    {
        __IO uint32_t CR;
        stc_bgr_cr_field_t CR_f;
    };
}M0P_BGR_TypeDef;

CR的最低位就是CR_f.BGR_EN,这两种赋值方式最终的效果是一致的,对照HC32F005的手册,这个BIT就是使能BGR模块:
在这里插入图片描述
看起来没有什么问题,实际debug时,发现执行赋值语句后,两种方式的值都是一致的,但是后续运行的结果就是不一致,给位域结构CR_f赋值的方式系统正常运行,而给CR赋值的方式就是会导致整个MCU systick停止,系统停止了任务调度,真是见鬼了。
但是想想,这个DDL库是从官网下载的,通常情况下应该不会有问题。再次细读datasheet,注意到了一个说法:
在这里插入图片描述
同时注意到V1.9.0的DDL库封装的函数中,确实延时了20us,然而原来寄存器操作的方式并没有延时操作,那么可能就是调用延时函数delay10us(2)造成的,虽然内心极度不相信,忐忑地查看了delay10us的实现:

/**
 * \brief   delay10us
 *          delay approximately 10us.
 * \param   [in]  u32Cnt
 * \retval  void
 */
void delay10us(uint32_t u32Cnt)
{
    uint32_t u32end;
    
    SysTick->LOAD = 0xFFFFFF;
    SysTick->VAL  = 0;
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_CLKSOURCE_Msk;
    
    while(u32Cnt-- > 0)
    {
        SysTick->VAL = 0;

        u32end = 0x1000000 - SystemCoreClock/100000;
        while(SysTick->VAL > u32end)
        {
            ;
        }
    }
    
    SysTick->CTRL = (SysTick->CTRL & (~SysTick_CTRL_ENABLE_Msk));
}

好家伙,居然用SysTick定时器来实现延时,LOAD值都被修改了,难怪SysTick失效了,耗费了几个小时居然是这样一个问题。
那么最后修改起来也就简单了,还是采用寄存器赋值的方式,重写延时函数:

	#if 0 // 1.9.0 ddl
    Bgr_BgrEnable();
	#else // 1.8.0 ddl
    M0P_BGR->CR_f.BGR_EN = 0x1u;	//使能内建电压模块
    M0P_BGR->CR_f.TS_EN = 0x0u;
	for( int i = 0; i < 320; i++ ){  // delay 20us 320*(1/16us) = 20us
		__ASM("NOP");
	}
	#endif

因为系统PCLK是16Mhz,每条指令执行时间为1/16us, 需要执行320次才能耗费20us,因此用一个for()来实现,同时由于i++,i<320这些判断也会产生额外的耗时,最后的时间肯定会大于20us,也保证了BGR模块可以正常操作。

最后为了证明修改后的延时代码是否如分析的那样,看看编译器生成的汇编代码片段:
在这里插入图片描述
C语言的for从第9行开始:
1,第 9行,编译器为局部循环控制变量i分配给了寄存器R0,并用MOVS指令初始化为0
2,第11行,B 0x000013c8指令跳转到第30行
3,第30行,31行指令给计算了出了一个数值255+65=320,并赋值给寄存器R1
4,第32行,比较R0和R1的值,即 i<320 ?
5,第33行,BLT 0x000013c4, 若R0小于R1,即 i < 320, 则跳转第19行,否则for()结束,进入后续逻辑代码块
6,第19行,执行我们定义的NOP指令,接着执行第28行,对R0加1,即i++,接着跳转第30行,继续下一轮比较。

逻辑上与C语言是一致的,只不过除了NOP指令,还增加了6条控制指令,这样320次循环执行下来,耗时必定超过了20us,在这里我只需要保证满足20us即可,时间长一点对系统影响不大。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值