织女星开发板RISC-V内核实现微秒级精确延时

前言

收到VEGA织女星开发板也有一段时间了,好久没玩了,想驱动个OLED屏,但是首先要实现IIC协议,而实现IIC协议,最基本的就是需要一个精确的延时函数,所以研究了一下如何来写一个精确的延时函数。众所周知,ARM Cortex-M内核都有一个24位的SysTick系统节拍定时器,它是一个简易的周期定时器,用于提供时基,多为操作系统所使用。RV32M1的RISC-V内核也有一个SysTick定时器,只不过它不属于内核,而是使用的一个外部通用定时器,即LPIT0( low power periodic interval timer)定时器的通道0来实现的,我们可以从system_RV32M1_ri5cy.c文件中获得一些信息:


/* Use LIPT0 channel 0 for systick. */
#define SYSTICK_LPIT LPIT0
#define SYSTICK_LPIT_CH 0
#define SYSTICK_LPIT_IRQn LPIT0_IRQn

/* Leverage LPIT0 to provide Systick */
void SystemSetupSystick(uint32_t tickRateHz, uint32_t intPriority)
{
    /* Init pit module */
    CLOCK_EnableClock(kCLOCK_Lpit0);

    /* Reset the timer channels and registers except the MCR register */
    SYSTICK_LPIT->MCR |= LPIT_MCR_SW_RST_MASK;
    SYSTICK_LPIT->MCR &= ~LPIT_MCR_SW_RST_MASK;

    /* Setup timer operation in debug and doze modes and enable the module */
    SYSTICK_LPIT->MCR = LPIT_MCR_DBG_EN_MASK | LPIT_MCR_DOZE_EN_MASK | LPIT_MCR_M_CEN_MASK;

    /* Set timer period for channel 0 */
    SYSTICK_LPIT->CHANNEL[SYSTICK_LPIT_CH].TVAL = (CLOCK_GetIpFreq(kCLOCK_Lpit0) / tickRateHz) - 1;

    /* Enable timer interrupts for channel 0 */
    SYSTICK_LPIT->MIER |= (1U << SYSTICK_LPIT_CH);

    /* Set interrupt priority. */
    EVENT_SetIRQPriority(SYSTICK_LPIT_IRQn, intPriority);

    /* Enable interrupt at the EVENT unit */
    EnableIRQ(SYSTICK_LPIT_IRQn);

    /* Start channel 0 */
    SYSTICK_LPIT->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << SYSTICK_LPIT_CH);
}

void SystemClearSystickFlag(void)
{
    /* Channel 0. */
    SYSTICK_LPIT->MSR = (1U << SYSTICK_LPIT_CH);
}

system_RV32M1_ri5cy.h文件中的SysTick中断服务函数:


#define SysTick_Handler LPIT0_IRQHandler

关于LPIT0

LPIT0的每个通道都包含一个32位的计数器,加载计数值之后开始倒数,当倒数到0时,中断标志位被置1,通过向中断标志位写1来清除中断。为了尽量减少执行函数所消耗的时间,delay延时函数的采用了直接操作寄存器的方式来实现。通过阅读RV32M1参考手册【Chapter 50 Low Power Interrupt Timer (LPIT)】章节,和fsl_lpit.h库函数的实现,我们可以了解到以下几个寄存器的功能:

寄存器名称 全称 读/写 含义
TVAL Timer Value Register 读/写 设置定时器初值
CVAL Current Timer Value 只读 获取当前计数值
SETEN Set Timer Enable Register 读写 定时器使能控制
CLRTEN Clear Timer Enable Register 只写 清除计数值
MCR Module Control Register 读写 定时器时钟使能控制
MSR Module Status Register 读写 溢出中断标志,写1清除中断

通过上面参考手册相关寄存器的介绍,我们有两种方式来获取定时器是否溢出:

  • 获取当前计数值是否为0,即CVAL寄存器的值
  • 获取寄存器状态是否溢出,即MSR寄存器的值。

这几个寄存器都是32位的,具体每一位的含义,可以查阅RV32M1参考手册

delay.c文件


#include "delay.h"

static uint8_t  fac_us=0;                           //us延时倍乘数
static uint16_t fac_ms=0;                           //ms延时倍乘数,在ucos下,代表每个节拍的ms数

void Delay_Init(void)
{
    CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcFircAsync);    //设置定时器时钟48MHz
    LOG("LPIT0: %ld \r\n", CLOCK_GetIpFreq(kCLOCK_Lpit0));  //输出LPIT0时钟

    CLOCK_EnableClock(kCLOCK_Lpit0);    //使能时钟
    LPIT_Reset(LPIT0);                  //复位定时器
    LPIT0->MCR = LPIT_MCR_M_CEN_MASK;   //使能定时器

    fac_us = CLOCK_GetIpFreq(kCLOCK_Lpit0)/1000000;
    fac_ms = fac_us*1000;
}
//基于SysTick即LPIT0实现的延时微秒函数
void Delay_us(uint32_t Nus)
{
    LPIT0->CHANNEL[kLPIT_Chnl_0].TVAL =  48 * Nus - 1;                  //加载时间
    LPIT0->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0);     //启动定时器
    while(LPIT0->CHANNEL[kLPIT_Chnl_0].CVAL);                           //等待计数值到0
//  while((LPIT0->MSR & 0x0001) != 0x01);                               //等待溢出
//  LPIT0->MSR |= (1U << kLPIT_Chnl_0);                                 //写1,清除中断
    LPIT0->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0);     //清除计数器
}

//基于SysTick即LPIT0实现的延时毫秒函数
void Delay_ms(uint32_t Nms)
{
    LPIT0->CHANNEL[kLPIT_Chnl_0].TVAL = Nms * fac_ms  - 1;          //加载时间
    LPIT0->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0); //启动定时器
    while(LPIT0->CHANNEL[kLPIT_Chnl_0].CVAL);                       //等待计数到0
//  while((LPIT0->MSR & 0x0001) != 0x0001);                         //等待产生中断
//  LPIT0->MSR |= (1U << kLPIT_Chnl_0);                             //向中断标志位写1,清除中断
    LPIT0->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0); //清除计数器
}

delay.h文件


#ifndef __DELAY_H__
#define __DELAY_H__

#include "fsl_lpit.h"
#include "fsl_lpit.h"
#include "fsl_debug_console.h"
#include "sys.h"

void Delay_Init(void);      //SysTick定时器,即LPIT0,时钟可设置
void Delay_ms(uint32_t Nms);
void Delay_us(uint32_t Nus);

#endif

实际验证


...

#include "delay.h"
...

int main(void)
{
    ...
    Delay_Init();
    ...
    while(1)
    {
        GPIOA->PTOR = 1 << 24;  //寄存器方式操作,减小误差
        Delay_ms(100);  //延时微秒函数验证
//      Delay_us(5);    //延时微秒函数验证
    }
}

通过实际示波器测试,发现Delay_us微秒级延时函数,无论延时多少时间都有2us左右的误差,不知道是这为什么,不过实现IIC协议驱动OLED屏并没有什么影响。

驱动IIC接口OLED

  • 社区首页的LOGO图片

  • OLED实际显示效果:

总结

既然精确延时函数实现了,那么各种协议的传感器、显示模块、通信模块的驱动都可以轻松实现了,希望分享的本篇帖子能给社区的朋友一些帮助,也希望大家能多多发帖,互相交流学习。

参考资料

历史精选



欢迎关注我的个人博客www.wangchaochao.top

或微信扫码关注我的公众号

原文地址:https://www.cnblogs.com/whik/p/11105346.html

时间: 2024-10-07 06:46:06

织女星开发板RISC-V内核实现微秒级精确延时的相关文章

织女星开发板使用RISC-V RI5CY核驱动GPIO

前言 织女星开发板是OPEN-ISA社区为中国大陆地区定制的一款体积小.功耗超低和功能丰富的 RISC-V评估开发板,基于NXP半导体四核异构RV32M1主控芯片. 两个RISC-V核:RI5CY + ZERO_RISCY. 两个ARM核: Cortex-M4F + Cortex-M0+ . 4个核被分为两个子系统,大核CM4F/RI5CY和小核CM0+/ZERO-RISCY,片上集成1.25 MB Flash .384 KB SRAM,其中1 MB的Flash被大核所使用,起始地址0x0000

织女星开发板使用RISC-V核驱动GPIO

前言 织女星开发板是OPEN-ISA社区为中国大陆地区定制的一款体积小.功耗超低和功能丰富的 RISC-V评估开发板,基于NXP半导体四核异构RV32M1主控芯片. 两个RISC-V核:RI5CY + ZERO_RISCY. 两个ARM核: Cortex-M4F + Cortex-M0+ . 4个核被分为两个子系统,大核CM4F/RI5CY和小核CM0+/ZERO-RISCY,片上集成1.25 MB Flash .384 KB SRAM,其中1 MB的Flash被大核所使用,起始地址0x0000

NXP恩智浦VEGA织女星开发板免费申请!

前言 大概两周前申请了一块NXP恩智浦的开发板,今天终于收到了!在这里推荐给大家,官方网站刚上线一个月左右,目前申请的人还不算多,感兴趣的朋友可以申请一个,体验一下这个四核性能怪兽.大厂就是大气,包装盒,开发板,Micro USB数据线,跳线帽一应俱全,全国包邮! 包装盒 开发板正面 重点是主控芯片--NXP推出的RV32M1异构4核MCU芯片,片上集成两个RISC-V架构内核和两个ARM架构内核,对于ARM架构大家再熟悉不过了,从学习工作经常玩的单片机STM32,到手机用的处理器联发科.高通骁

windows下实现微秒级的延时

1.微秒级的延时肯定不能基于消息(SetTimer函数),因为一出现消息堵塞等就会影响精度,而且setTimer单位才是毫秒.实际响应时间可能要到55毫秒左右. 2.微秒级的延时也不能不能基于中断,VxD最快的时钟服务程序Set_Global_Time_Out函数才能保证1毫秒的精度.其他挂接int 8H中断处理函数等,只能保证55ms的精度.(有时还不能) 3.因此可以想到汇编下的那种基于循环执行语句的那种延时.但汇编那种代码不通用,跟cpu的频率有关. 所以可以用windows下的几个函数来

荣品RP4412开发板烧写内核cannot load出错的原因

问:荣品RP4412开发板烧写必须要配置Xmanager吗? 现在我烧写内核出现这个错误是什么原因呢? 答:4412文件夹下没有zImage这个文件, 你打开4412这个文件夹. 你都拼写错了, zImage, 你打成了zImag. 问: 现在出现这个提示了,是正确的吗? 答:你需要另外安装驱动. 下个360手机助手, 然后装上驱动后,在重头来吧. 问:哦,但是我用豌豆荚可以连接到开发板,什么原因? 答:这个是因为你的电脑不识别fastboot状态下的硬件. 就在目前的状态下, 安装USB驱动,

迅为iTOP-iMX6开发板-设备树内核-中断例程

1.1 中断概述 ARM 架构 linux 内核中,有 5 种常见的异常,其中中断异常是其一,Linux 内核将所 有中断统一编号,使用一个 irq_desc 结构体来描述这些中断,里面记录了中断名称.中断状 态.中断标记.并提供了中断的底层硬件访问函数(如:清除.屏蔽.使能中断),提供了这 个中断的处理函数入口,通过它还可以调用用户注册的的中断处理函数.linux 内核的中断体 系已经很完善了,驱动工程师需要做的就是调用 request_irq 函数向内核注册中断处理函 数,下面我们来看看 r

FS4412开发板移植Linux内核4.9.9版本

基本移植 1.源码下载 官网下载最新的linux4.9.9内核:https://mirrors.edge.kernel.org/pub/linux/kernel/. 第三个数字是修订版本号. 2.解压并配置 tar xvf linux-4.9.9.tar.xz 进入内核源码目录及: cd linux-4.9.9 修改编译工具链,打开顶层的Makefile: vi Makefile 修改: ARCH ?= $(SUBARCH) CROSS_COMPILE ?= $(CONFIG_CROSS_COM

【ST开发板评测】使用Python来开发STM32F411

前言 板子申请了也有一段时间了,也快到评测截止时间了,想着做点有意思的东西,正好前一段时间看到过可以在MCU上移植MicroPython的示例,就自己尝试一下,记录移植过程. MicroPython是什么 程序猿中有句俗语:人生苦短,我用Python.Python的强大和易用性让它不仅可以写网站,编程序,在嵌入式领域也有一席之地. MicroPython,是Python3编程语言的一个完整软件实现,包括Python标准库的一小部分,用C语言编写,经过优化可在微控制器和受限环境中运行.MicroP

兆易创新首款RISC-V开发板——GD32VF103-EVAL开箱评测

开箱Vlog B站评测视频:https://www.bilibili.com/video/av81164593/ Hello,各位朋友大家好!今天我们来开箱兆易半导体的一款RISC-V开发板--GD32VF103V-EVAL.今年可以说是RISC-V比较火的一年,关注RISC-V的朋友可能都知道,2019年8月份的时候,兆易创新发布了国内第一款基于RISC-V内核的32位通用MCU--GD32VF103系列,而我今天拿到的这块板子就是基于GD32VF103的一块EVAL板,也就是评估板,相比于另