CMT2380/HC32L110入门踩坑记录
写在前面
本帖子不定期更新,记录本人遇到的问题。CMT230F32的微控单元是基于HC32L110。
1.空白工程启动文件的问题
使用官网空白工程CMT2380F32按照自己需求的工程时,需要注意到启动文件startup_CMT2380F32.s和官网给的example里面的启动文件是不一样的,官方可能后面改了也没注意到。
主要是启动文件中对中断服务函数的定义有差别,导致无法触发任何中断函数。
解决方案是打开dll.h注释掉所有中断服务函数宏定义(因为启动文件定义过了,而后面旧版本的中断服务函数在这个工程里已经没有了)
2.RTC时钟问题
RTC时钟默认使用外部晶振,但是CMT2380的外部晶振给了射频,需要手动初始化内部RCL,否则RTC读取到的时间不变(初始化也不返回错误)可以考虑这样初始化,使能内部RCL不会影响系统内部时钟。
Clk_SetRCLFreq(ClkFreq32768);
Clk_Enable(ClkRCL, TRUE);
Clk_SetPeripheralGate(ClkPeripheralRtc,TRUE);
3.UART格式化输出的问题
HC32L110使用格式化输出的方法官方有教程。我这里用了微库,但是默认使用uart0作为调试串口,如果使用其他uart需要修改库文件。
解决方案:打开dll.c,修改Debug_Output(uint8_t u8Data),将所有的uart0都换成uart1(或者其他的)
void Debug_Output(uint8_t u8Data)
{
M0P_UART1->SCON_f.REN = 0;
M0P_UART1->SBUF = u8Data;
while (TRUE != M0P_UART1->ISR_f.TI)
{
;
}
M0P_UART1->ICR_f.TICLR = 0;
}
4.SysTick进行延时
问题描述:改变RCH频率后官方库的延时函数延时错误
在启动文件里,RCH默认是4MHz,官方库给了2个延迟函数利用SystemCoreClk记录RCH主频,且这个值默认是4MHz。
当我们改变时钟频率后,不会自动更新SystemCoreClk,于是延时错误,如我用16M会比正常时间快四倍。
解决方法有二个:
1。在你修改过时钟后,加上
SystemCoreClock=Clk_GetHClkFreq();
//或者这个
SystemCoreClockUpdate();
2.将delay1ms函数中的SystemCoreClock替换成Clk_GetHClkFreq()
5.SW调试卡住或运行后卡住
问题描述:函数参数的指针是错的或是空指针,在调试中会卡在某一段代码,但不一定是卡在你函数调用的那一段代码,通常是直接卡在调用函数的代码前。
这个问题对printf也会生效,如
f1();
f2();
printf("%d",a[0]);
假如数组a没有初始化,,那很可能在f1或f2就卡住了。即便已经初始化了,如果之后没有再用到数组a(没有在主循环出现),很可能会卡住,通常会卡在f1或者f2导致无法分析问题出在哪。
除了自己操作失误以外,很有可能是编译器给优化掉了。
建议这样选择,然而实际测下来还不够,要把重要的指针设为全局。
6.printf和串口接收中断的冲突
当使用微库的printf时,这样接收串口助手发送的一串字符(接收一个字符中断一次,一共中断7次)。假如说在程序的其他地方用了printf以后,会发现只能接收到一个字符串,然后再也无法进入Uart接收中断。
例如,这样用printf输出接收到的字符串,仅能输出一次,然后经过调试发现,继续向HC32L110输入字符串,再也没有进入中断。其原因不明。
if(u8RxFlg)
{
u8RxFlg = 0;
i=1;
printf("%s\n",u8RxData);
M0P_UART1->ICR_f.RICLR = 0;
}
但经过测试,发现printf用重定向的方法则不存在这个问题。
在dll.c里面注释掉fputc函数,然后在main里面重写它。详细可以参考这篇https://blog.csdn.net/willOkay/article/details/106819262
int fputc(int ch, FILE *f)
{
Uart_SetTb8(UARTCH1,Even,ch);
Uart_SendData(UARTCH1,ch);
return ch;
}
改为重定位的方法后,printf和uart接收中断不再冲突,但是注意不能在接收的过程用printf,比如在中断函数中不要使用printf、在七次中断的中间也尽量不使用。
7.16位低功耗计时器LPT实现更长计时
HC32L110的LPT为16位,向上计数,因此假如说你要定时x毫秒,理论公式是0xFFFF-x32.768,由于不支持浮点数,因此实际寄存器里写理论公式是0xFFFF-x32。由于忽略了小数部分,这样做误差很大。无法用算法补偿的情况下,比如用低功耗计时器从深度休眠状态定时唤醒MCU。可以采用100ms单位的计数方式,即只计数100毫秒的整数倍,其余的时间通过通用计时器实现,计数100ms的方法如下:
void MySetLPT100ms(uint16_t ms100)
{
//Lpt 中断使能
Lpt_ClearIntFlag();
Lpt_EnableIrq();
EnableNvic(LPTIM_IRQn, 3, TRUE);
Lpt_ARRSet(0xFFFF-3276*ms100);
Lpt_Run();
}
void MydisableLPT(void)
{
Lpt_ClearIntFlag();
EnableNvic(LPTIM_IRQn,3,FALSE);
Lpt_Stop();
}
void SetSleepOnce(uint16_t Sleeptime)
{
#ifdef EnableSleep
MySetLPT100ms(Sleeptime);
SystemSleep();
MydisableLPT();
#endif
}
但是这样做一次计时的上限是19百毫秒,假如说计时时间很长,要超过2秒的话,就不能一次解决。
以计时3秒钟为例,如果这样写,实际计时偶尔会出现错误,多数时候是成功执行了3次100ms计时,偶尔只会成功执行2次计时。
SetSleepOnce(10);
SetSleepOnce(10);
SetSleepOnce(10);
猜想是硬件根据寄存器调整需要时间。需要在相邻两次计时间加入延迟,我的定时函数是这样写的,可以实现256*10百毫秒的定时,uint8改了也可以更长:
uint8_t LPTtimerset(uint8_t tim)
{
if(tim>10)
{
SetSleepOnce(10);//只睡眠100的整数倍
delay100us(1);//延迟,避免错误
return LPTtimerset(tim-10);
}else
{
SetSleepOnce(tim);//只睡眠100的整数倍
return tim;
}
}