打印
[综合信息]

华大HC32F005 Systick问题

[复制链接]
2271|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
磨砂|  楼主 | 2021-8-1 15:16 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
在项目中使用了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即可,时间长一点对系统影响不大。


使用特权

评论回复
沙发
chenjun89| | 2021-8-3 07:51 | 只看该作者
难道是不兼容?

使用特权

评论回复
板凳
martinhu| | 2021-8-3 09:09 | 只看该作者
BGR函数里面用了个10us的延时函数,这个函数是用Systick计数实现的。
如果你要用Systick,建议修改delay10us()和delay1ms()两个函数,在ddl.c里面,这样就不冲突了

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

    delay10us(2);
}

使用特权

评论回复
地板
liu252799093| | 2022-2-13 22:07 | 只看该作者
MARK,华大的库还得改进改进

使用特权

评论回复
5
duo点| | 2022-2-18 16:43 | 只看该作者
是不是哪里不兼容

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

3976

帖子

2

粉丝