本帖最后由 werasd 于 2022-5-25 15:05 编辑
#技术资源#
四月份合泰入驻21ic,有领开发板的活动。申请了一块,有幸通过审核,板子已经收到。
接触单片机单纯因为喜欢,来回折腾,水平没啥长进,但审美确实变得挑剔。一般的板子现在真的提不起什么兴趣,但是这个板子不一样,它有小海豚啊!灵兽懂不懂? 大伙儿都知道,要在PCB上看见个动物,多难的事情!今天这个教程,就冲这个海豚。
————以下正文部分———— CoreMark是常用的评价微控制器性能的指标之一,该工具通过列表处理、矩阵处理、状态机和CRC等测试给出微控制器的性能评价。由于最终结果以分数的形式直观展现,为比较不同微控制器、不同编译工具的性能提供了参考手段。该工具可以免费获取,官网有大量的微控制器评分数据可供查询。从我第一次接触CoreMark到现在,官网已改版过三次,这次好像是庆祝EEMBC 25周年。 要想将CoreMark移植到任何的微控制器其实都不难,要准备的事情无非三步:第一步,实现一种不占用内核资源的计时手段,要求该功能启停可调用函数控制,通常使用外设定时器配合中断实现。第二步,实现向外部打印信息的手段,重定向printf函数,在打印评价结果的时候需要用到,上串口TX就行。第三步,获取CoreMark源码包,逐步移植到HT32F52352。全文内容比较长,分上下部分,上半截介绍前两步,CoreMark移植放到下半截。
开发环境搭建可以参考官方文档《Keil MDK-ARM快速入门指南适用于Holtek HT32系列单片机》,在获取到标准固件库后,可以根据工程模板新建自己的工程。我使用的固件库版本是HT32_STD_5xxxx_FWLib_V1.0.25_5831,需要的文件在library、project_template和utilities文件夹下。 内核头文件路径: HT32_STD_5xxxx_FWLib_V1.0.25_5831\library\CMSIS\Include 启动文件startup_ht32f5xxxx_01.s路径: HT32_STD_5xxxx_FWLib_V1.0.25_5831\library\Device\Holtek\HT32F5xxxx\Source\ARM 固件库文件路径: HT32_STD_5xxxx_FWLib_V1.0.25_5831\library\HT32F5xxxx_Driver 注意添加ht32f5xxxx_01_usbdconf.h,否则提示文件缺失,可从下面的路径获取: HT32_STD_5xxxx_FWLib_V1.0.25_5831\project_template\IP\Template 其它需要的文件1:ht32f5xxxx_01.h、system_ht32f5xxxx_01.c、system_ht32f5xxxx_01.h路径: HT32_STD_5xxxx_FWLib_V1.0.25_5831\library\Device\Holtek\HT32F5xxxx 其它需要的文件2:ht32f5xxxx_conf.h路径: HT32_STD_5xxxx_FWLib_V1.0.25_5831\project_template\IP\Template 其它需要的文件3:HT32F5xxxx_01_DebugSupport.ini路径: HT32_STD_5xxxx_FWLib_V1.0.25_5831\project_template\IP\Template\MDK_ARMv5 将以上文件添加至工程中,main.c和ht32f5xxxx_01_it.c文件从工程模板复制即可。工程内文件结构如下图。 新建工程的相关设置需要参考模板,打开任意工程模板,照搬过来即可;注意工程模板中未使用到的一部分固件库文件也需要从新建工程中移除,否则可能报错。以上步骤完成后即可进行编译,如遇到个别小问题可查看编译信息或对照工程模板进行排查。
————点灯测试新建工程是否成功———— 查看电路原理图,得知LED2对应PC15,GPIO初始化代码如下:
void LED_CFG(void)
{
AFIO_GPxConfig(GPIO_PC, GPIO_PIN_15, AFIO_FUN_GPIO);
GPIO_PullResistorConfig(HT_GPIOC, GPIO_PIN_15, GPIO_PR_DOWN);
GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_15, RESET);
GPIO_DirectionConfig(HT_GPIOC, GPIO_PIN_15, GPIO_DIR_OUT);
}
注意时钟使能可以在自带的CKCU_Configuration()函数中完成,设置指定位即可:CKCUClock.Bit.PC=1;使用GPIO_WriteOutBits()函数测试LED2,效果如下:
使用HT32F52352的BFTM0可以非常简单的实现定时功能。首先BFTM0的时钟使能在CKCU_Configuration()函数中通过指定CKCUClock.Bit.BFTM0=1来完成。配置BFTM0每0.5ms中断一次,在中断内翻转PC15的电平,可以得到周期为1ms的方波。配置函数如下: void BFTM_CFG(void)
{
BFTM_SetCompare(HT_BFTM0, SystemCoreClock / 2000);
BFTM_SetCounter(HT_BFTM0, 0);
BFTM_IntConfig(HT_BFTM0, ENABLE);
NVIC_EnableIRQ(BFTM0_IRQn);
}
BFTM0中断函数可以写到ht32f5xxxx_01_it.c文件内:
void BFTM0_IRQHandler(void)
{
BFTM_ClearFlag(HT_BFTM0);
GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_15,!GPIO_ReadOutBit(HT_GPIOC, GPIO_PIN_15));
}
使用示波器观察波形,确认方波周期为1ms,中断定时成功。
————重定向printf到串口USART1———— HT32F52352的串口1与板上调试器相连,可通过Holtek USB虚拟串口连接到PC。初始化USART1的TX引脚PA4,设置参数:115200,8,1,0;代码如下: void USART_CFG(void)
{
AFIO_GPxConfig(GPIO_PA, GPIO_PIN_4, AFIO_FUN_USART_UART);
USART_InitTypeDef USART_InitStructure = {0};
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WORDLENGTH_8B;
USART_InitStructure.USART_StopBits = USART_STOPBITS_1;
USART_InitStructure.USART_Parity = USART_PARITY_NO;
USART_InitStructure.USART_Mode = USART_MODE_NORMAL;
USART_Init(HT_USART1, &USART_InitStructure);
USART_TxCmd(HT_USART1, ENABLE);
}
注意USART1的时钟使能在CKCU_Configuration()函数中通过指定CKCUClock.Bit.USART1=1来完成。
测试串口能否正常发送数据,使用如下代码发送‘H’字符来检查: while (USART_GetFlagStatus(HT_USART1, USART_FLAG_TXC) == RESET);
USART_SendData(HT_USART1, 'H');
确认串口正常工作后,尝试将printf重定向到串口1;工程模板中已有串口重定向的实现,该实现针对更为普遍的情况,重写了printf和scanf的一些子函数,初学者看起来可能比较复杂;若使用Keil的MicroLIB,则仅需要重写fputc()函数即可完成重定向;代码如下: int fputc(int ch, FILE *f)
{
while (USART_GetFlagStatus(HT_USART1, USART_FLAG_TXC) == RESET);
USART_SendData(HT_USART1, ch);
return ch;
}
使用printf("HT32F52352 USART1 TX.\r\n");测试,效果如下
————移植CoreMark到HT32F52352———— 接下来开始移植CoreMark。
第一步:从CoreMark官网下载软件包,提取文件到HT32F52352工程中。CoreMark软件包文件结构如下图左侧所示,已准备好的软件工程如右侧所示。在软件工程中新建CoreMark文件夹,将其添加到include路径中。将左侧绿色框中,将core_list_join.c、core_main.c、core_matrix.c、core_state.c、core_util.c、coremark.h等文件复制到CoreMark文件夹里。新建CoreMark组,加入上述文件,打开左侧simple文件夹,将core_portme.c、core_portme.h文件复制到USER文件夹中。
第二步:CoreMark在core_main.c文件中提供了main()函数,该main()函数运行时通过调用core_portme.c中的portable_init()函数实现MCU初始化。因此需要将MCU初始化代码移动到到portable_init()函数里并删除原有main.c文件。提供移动好的portable_init()函数供参考。 void portable_init(core_portable *p, int *argc, char *argv[])
{
if (sizeof(ee_ptr_int) != sizeof(ee_u8 *)) {
ee_printf("ERROR! Please define ee_ptr_int to a type that holds a pointer!\n");
}
if (sizeof(ee_u32) != 4) {
ee_printf("ERROR! Please define ee_u32 to a 32b unsigned type!\n");
}
p->portable_id=1;
NVIC_Configuration();
CKCU_Configuration();
USART_CFG();
BFTM_CFG();
printf("CoreMark Start.\r\n");
}
第三步:在BFTM0中断中调整中断频率为1kHz,获得时间间隔1ms的计时器,即每1ms进入中断一次。修改core_portme.c里这个宏定义,以匹配每秒1000个中断计数。 #define EE_TICKS_PER_SEC 1000
此处中断频率不一定非得是1kHz,关键是要提供每秒的等价中断计数,即要让程序知道多少个中断计数对应1秒的时间。与定时相关的函数有三个,void start_time(void);void stop_time(void)和CORE_TICKSget_time(void);分别用于启动、停止定时器,获取时间。数据类型CORE_TICKS就是unsigned long。为方便操作,可将计数变量设置为全局变量,通过extern关键字直接访问。相关实现如下:
extern unsigned long time_ticks_ms;
void start_time(void) {
BFTM_EnaCmd(HT_BFTM0, ENABLE);
}
void stop_time(void) {
BFTM_EnaCmd(HT_BFTM0, DISABLE);
}
CORE_TICKS get_time(void) {
return time_ticks_ms;
}
中断函数简单写即可:
unsigned long time_ticks_ms=0;
void BFTM0_IRQHandler(void)
{
time_ticks_ms++;
BFTM_ClearFlag(HT_BFTM0);
}
第四步:注释部分core_portme.c中不使用的代码,修改迭代次数(将ITERATIONS用数字替换,若迭代次数太少导致测试时间少于10s,则会报错;经测试48MHz下,ITERATIONS=1000耗时约11秒)//volatile ee_s32 seed4_volatile = ITERATIONS;
volatile ee_s32 seed4_volatile = 1000;
第五步:修改堆栈大小,否则会触发HardFault:将编译好的程序烧录进开发板,等待测试完成,结果如下:HT32F52352,主频48MHz,跑分成绩90.84分!
工程代码和CoreMark源码已通过附件分享,如有需要可下载参考。
|