【沁恒微CH32V307评估板试用体验】Delay_Ms函数分析(SysTick滴答定时器) - RISC-V MCU技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

刘泽文 关注 私信
[文章]

【沁恒微CH32V307评估板试用体验】Delay_Ms函数分析(SysTick滴答定时器)

0. 前言

之前使用ArduinoM3内核的MCU时,发现有一种非阻塞程序流程,例如Arduinomills()函数,STM32HAL_GetTick()都可以获取芯片启动到现在的时间,这样我们就能在一个while(1)里面完成各个传感器的数据获取,举例如下:

/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { //类似于Arduino中ESP8266时间mills()的写法? if ((HAL_GetTick() - tick >= 100)) { tick = HAL_GetTick(); u8g2_ClearBuffer(&u8g2); sprintf((char*)Message, "Encoder"); u8g2_DrawUTF8(&u8g2, 40, 16 - 2, (char*)Message); sprintf((char*)Message, "%d", enc_key_value_buff); u8g2_DrawStr(&u8g2, 110, 16 - 2, (char*)Message); MessageLength = sprintf((char*)Message, "Counter: %d ", (int)htim1.Instance->CNT); HAL_UART_Transmit(&huart2, Message, MessageLength, 100); u8g2_DrawStr(&u8g2, 15, 32 - 4, (char*)Message); UpdateAudioVolume(); MessageLength = sprintf((char*)Message, "Volume: %d ", AudioVolume); HAL_UART_Transmit(&huart2, Message, MessageLength, 100); u8g2_DrawStr(&u8g2, 15, 48 - 6, (char*)Message); u8g2_DrawRBox(&u8g2, 14, 50, AudioVolume, 12, 4); u8g2_SendBuffer(&u8g2); } /* 1ms 按键扫描 */ if (HAL_GetTick() - tick1 >= 1) { tick1 = HAL_GetTick(); key_check_all_loop_1ms(); } /* Key按键按下查询 */ if (HAL_GetTick() - tick2 >= 10) { tick2 = HAL_GetTick(); enc_key_value = key_read_value(); if (enc_key_value == KEY0_UP_SHORT) { enc_key_value_buff = 1; } else if (enc_key_value == KEY0_UP_DOUBLE) { HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin); enc_key_value_buff = 2; } else if (enc_key_value == KEY0_LONG) { enc_key_value_buff = 3; } } if (HAL_GetTick() - tick3 >= 50) { tick3 = HAL_GetTick(); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }

1. SysTick

CH32V307手册下载地址

本次我们去查看CH32V307的手册,看到对SysTick有如下描述:

systick描述.png

查看系统时钟树结构,我们发现SysTick的时钟源来自于HCLK,使用中大多/8分频,即SysTick=HCLK/8,后面我们会在代码中看到。

时钟树.png

2. 源码分析

我们打开工程项目下的debug.c,里面包含了Delay函数的初始化及实现。

SysTick的时钟分频设置在system_ch32v30x.c中的SetSysClockToXX()函数中,其中XX为当前系统时钟SYSCLK_FREQ,通过#define进行设置。

  • 首先看初始化函数Delay_Init
/********************************************************************* * @fn Delay_Init * * [url=home.php?mod=space&uid=2666770]@Brief[/url] Initializes Delay Funcation. * * [url=home.php?mod=space&uid=1141835]@Return[/url] none */ void Delay_Init(void) { p_us = SystemCoreClock / 8000000;// 72m/8000000=9 p_ms = (uint16_t)p_us * 1000;// 9*1000 = 9000 }

其中SystemCoreClock / 8000000;中的8_000_000为上文中提到的8分频,乘以s->us的1_000_000单位转换,这样就可以得到每个us,SysTick的计数次数。同理,ms的计算一样,用us的乘以1000即可。

  • 接下来看看Delay_Ms的实现:
/********************************************************************* * @fn Delay_Ms * * @brief Millisecond Delay Time. * * [url=home.php?mod=space&uid=3142012]@param[/url] n - Millisecond number. * * @return None */ void Delay_Ms(uint32_t n) { uint32_t i; // 清除比较计数器结果 SysTick->SR &= ~(1 << 0); // 得到需要计数的时钟数目 i = (uint32_t)n * p_ms; // 设置比较计数器计数次数 SysTick->CMP = i; // 向下计数,设置输出比较值,启动系统计数器 STK SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0); // 等待比较计数器比较结果为1 while((SysTick->SR & (1 << 0)) != (1 << 0)) ; // 停止系统计数器 STK SysTick->CTLR &= ~(1 << 0); }

可以看到共使用了SR/CMP/CTLR三个寄存器,我们查询【手册】,查看寄存器定义:

  1. STK_CTLR 寄存器

CRLR寄存器.png

  1. SR寄存器

SR寄存器.png

  1. CMP寄存器分为CMPLRCMPHR

CMPLR寄存器.png

CMPHR寄存器.png

4. 最后,附上debug.c的代码:

  • debug.c
/********************************** (C) COPYRIGHT ******************************* * File Name : debug.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : This file contains all the functions prototypes for UART * Printf , Delay functions. * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * SPDX-License-Identifier: Apache-2.0 *******************************************************************************/ #include "debug.h" static uint8_t p_us = 0; static uint16_t p_ms = 0; /********************************************************************* * @fn Delay_Init * * @brief Initializes Delay Funcation. * * @return none */ void Delay_Init(void) { p_us = SystemCoreClock / 8000000;// 72m/8000000=9 p_ms = (uint16_t)p_us * 1000;// 9*1000 = 9000 } /********************************************************************* * @fn Delay_Us * * @brief Microsecond Delay Time. * * @param n - Microsecond number. * * @return None */ void Delay_Us(uint32_t n) { uint32_t i; SysTick->SR &= ~(1 << 0); i = (uint32_t)n * p_us; SysTick->CMP = i; SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0); while((SysTick->SR & (1 << 0)) != (1 << 0)) ; SysTick->CTLR &= ~(1 << 0); } /********************************************************************* * @fn Delay_Ms * * @brief Millisecond Delay Time. * * @param n - Millisecond number. * * @return None */ void Delay_Ms(uint32_t n) { uint32_t i; // 清除比较计数器结果 SysTick->SR &= ~(1 << 0); // 得到需要计数的时钟数目 i = (uint32_t)n * p_ms; // 设置比较计数器计数次数 SysTick->CMP = i; // 向下计数,设置输出比较值,启动系统计数器 STK SysTick->CTLR |= (1 << 4) | (1 << 5) | (1 << 0); // 等待比较计数器比较结果为1 while((SysTick->SR & (1 << 0)) != (1 << 0)) ; // 停止系统计数器 STK SysTick->CTLR &= ~(1 << 0); }

更多回帖

×
发帖