文章目录

  • 前言
  • 关于LPIT0
  • ZERO核的SysTick定时器
  • delay.c文件
  • delay.h文件
  • 实际验证
  • 驱动IIC接口OLED
  • 总结
  • 参考资料
  • 历史精选

前言

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

system_RV32M1_ri5cy.c文件的SysTick定时器:


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

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


#define SysTick_Handler LPIT0_IRQHandler

system_RV32M1_zero_riscy.c文件中的SysTick定时:


/* Use LIPT1 channel 0 for systick. */
#define SYSTICK_LPIT LPIT1
#define SYSTICK_LPIT_CH 0
#define SYSTICK_LPIT_IRQn LPIT1_IRQn

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

/
#define SysTick_Handler LPIT1_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参考手册

ZERO核的SysTick定时器

虽然同样是属于RISC-V内核,ZERO核与RI5CY核使用的SysTick定时器不同,

  • ZERO : LPIT1_CH0
  • RI5CY: LPIT0_CH0

可以通过预编译指令来进行条件编译,官方的Demo工程通过使用不同的宏定义来区分两个工程。

  • ZERO核宏定义:CPU_RV32M1_zero_riscy
  • RI5CY核宏定义:CPU_RV32M1_ri5cy

delay.c文件


#include "delay.h"/** ZERO : LPIT1_CH0* RI5CY: LPIT0_CH0* */static uint8_t  fac_us=0;
static uint16_t fac_ms=0;#if defined(CPU_RV32M1_zero_riscy)/** RISC_V ZERO 使用 LPIT1_CH0作为SysTick,与RI5CY不同* */void Delay_Init(void)
{CLOCK_SetIpSrc(kCLOCK_Lpit1, kCLOCK_IpSrcFircAsync);   //设置定时器时钟48MHzLOG("LPIT1时钟: %ld \r\n", CLOCK_GetIpFreq(kCLOCK_Lpit1));    //输出LPIT0时钟CLOCK_EnableClock(kCLOCK_Lpit1); //使能时钟LPIT_Reset(LPIT1);                    //复位定时器LPIT1->MCR = LPIT_MCR_M_CEN_MASK;    //使能定时器fac_us = CLOCK_GetIpFreq(kCLOCK_Lpit1)/1000000;fac_ms = fac_us*1000;
}void Delay_us(uint32_t Nus)
{LPIT1->CHANNEL[kLPIT_Chnl_0].TVAL =  48 * Nus - 1;                 //加载时间LPIT1->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0);     //启动定时器while(LPIT1->CHANNEL[kLPIT_Chnl_0].CVAL);                         //等待计数值到0
//  while((LPIT1->MSR & 0x0001) != 0x01);                               //等待溢出
//  LPIT0->MSR |= (1U << kLPIT_Chnl_0);                                   //写1,清除中断LPIT1->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0);       //清除计数器
}void Delay_ms(uint32_t Nms)
{LPIT1->CHANNEL[kLPIT_Chnl_0].TVAL = Nms * fac_ms  - 1;         //加载时间LPIT1->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0); //启动定时器while(LPIT1->CHANNEL[kLPIT_Chnl_0].CVAL);                     //等待计数到0
//  while((LPIT1->MSR & 0x0001) != 0x0001);                         //等待产生中断
//  LPIT0->MSR |= (1U << kLPIT_Chnl_0);                               //向中断标志位写1,清除中断LPIT1->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0); //清除计数器
}#elif defined(CPU_RV32M1_ri5cy)/** RISC_V RI5CY 使用 LPIT0_CH0作为SysTick,与ZERO不同* */void Delay_Init(void)
{CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcFircAsync);   //设置定时器时钟48MHzLOG("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;
}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);       //清除计数器
}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); //清除计数器
}#endif

delay.h文件


#ifndef __DELAY_H__
#define __DELAY_H__#include "fsl_lpit.h"
#include "fsl_debug_console.h"
#include "sys.h"/** ZERO : LPIT1_CH0* RI5CY: LPIT0_CH0* */void Delay_Init(void);
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实际显示效果:

总结

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

参考资料

  • RV32M1参考手册

历史精选

  • 真正的RISC-V开发板——VEGA织女星开发板开箱评测
  • 手把手教你搭建织女星开发板RISC-V开发环境
  • 织女星开发板启动模式修改——从ARM M4核启动
  • 织女星开发板调试器升级为Jlink固件
  • NXP恩智浦VEGA织女星开发板免费申请!
  • 国产处理器的逆袭机会——RISC-V

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

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

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

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

    文章目录 前言 准备工作 寄存器简介 GPIO配置PCR寄存器 GPIO控制寄存器 库函数简介 PORT_SetPinConfig PORT_SetPinMux GPIO_PinInit GPIO_W ...

  2. 真正的RISC-V开发板——VEGA织女星开发板开箱评测

    文章目录 前言 关于RISC-V架构 关于VEGA织女星开发板 基于FPGA实现的RISC-V开发板 基于RISC-V芯片实现的开发板 开箱爆照 板载资源简介 主控芯片RV32M1简介 支持的开发工具 ...

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

    前言 大概两周前申请了一块NXP恩智浦的开发板,今天终于收到了!在这里推荐给大家,官方网站刚上线一个月左右,目前申请的人还不算多,感兴趣的朋友可以申请一个,体验一下这个四核性能怪兽.大厂就是大气,包装 ...

  4. 织女星开发板能移植linux吗,织女星开发板启动模式修改——从ARM M4核启动

    前言 刚开始玩织女星开发板的时候,想先从熟悉的ARM核入手,连上Jlink,打开MDK版本的Demo程序,编译OK,却检测不到芯片,仔细看了一下文档,原来RV32M1芯片默认从RISC-V核启动,如果 ...

  5. 织女星开发板调试器升级为Jlink固件

    文章目录 前言 准备工作 升级操作 升级Jlink驱动 板载接口的说明 历史精选 前言 为了能使用板载的FreeLink调试器来调试RISC-V内核,我们需要把默认的CMSIC-DAP固件,升级为JL ...

  6. mssql 无法启动调试器 数据为空_织女星开发板启动模式修改——从ARM M4核启动

    织女星开发板启动模式修改--从ARM M4核启动 前言 刚开始玩织女星开发板的时候,想先从熟悉的ARM核入手,连上Jlink,打开MDK版本的Demo程序,编译OK,却检测不到芯片,仔细看了一下文档, ...

  7. 如何将RT-Thread移植到织女星开发板?

    上个学期天津大学的吕卫老师,在他<微处理器系统>课程的实验环节上,让研究生们使用织女星开发板做了一些项目. 本文是其中一份工作,由国际工程师学院,电子与通信工程的徐扬扬和张千依同学共同完成 ...

  8. 将linux内核烧进arm板,ARM开发板上uClinux内核移植

    <ARM开发板上uClinux内核移植>由会员分享,可在线阅读,更多相关<ARM开发板上uClinux内核移植(19页珍藏版)>请在人人文库网上搜索. 1.纷傲掌秀悸篷益哑檀扬 ...

  9. 织女星开发板RISC-V核通过SPI协议驱动ARDUINO LCD模块(显示)

    前言 第一次写这个博客,算是新手吧,刚好有这个机会,手边有VEGA的开发板和Arduino的LCD模块,做了点小东西,想和大家分享一下. 一开始只是想着通过SPI协议初始化LCD屏幕,然后发个字符串就 ...

最新文章

  1. mysql主从 1050错误
  2. 《PHP精粹:编写高效PHP代码》——第1章面向对象编程
  3. C++中map的使用
  4. python-二级补充-idle的了解
  5. 为什么要返回function_宇宙飞船返回时,为什么要冒着烧毁的风险加速穿过大气层?...
  6. golang中container/list包中的坑
  7. 大数据之-hadoop知识体系架构---大数据之hadoop工作笔记0001
  8. 微软将取缔 8 亿 Windows 10 用户的密码!
  9. 轻轻松松统计代码行数
  10. C#获取上传文件的扩展名
  11. 软件测试——测试用例设计方法
  12. 实现写邮箱html页面,用html写的简单的邮箱登陆界面
  13. 延安.居民家庭计算机普及率,2004~2014年家庭互联网普及率及电脑持有率
  14. 详解文本格式(Text)[第二天]
  15. 深度学习基础6(微分,偏导,梯度,链式法则)
  16. maximo跟java_Maximo7.5远程调用maximo的手动输入节点工作流
  17. 2018ICPC焦作站 B - Ultraman vs. Aodzilla and Bodzilla(贪心)
  18. java 视频压缩_Java 压缩视频(无需安装插件)
  19. android 如何让应用程序在全部应用程序列表里显示跟隐藏!
  20. 遗传算法解整数规划IntCon

热门文章

  1. centos下安装teamviewer
  2. 编译cc1:all warnings being treated as errors
  3. Windows10系统下MPI编程环境配置(超级详细)
  4. 小明最近迷上了化学,几乎天天在实验室做实验,但是很多实验生成的化学产物的相对分子质量令他很困惑,不知如何计算,请你编程帮他计算
  5. 网页设计经典色彩搭配
  6. error: previous declaration of '****' was here
  7. 14.State-理解原理即可、Flink中状态的自动管理、无状态计算和有状态计算、状态分类、Managed State Raw State\Keyed StateOperator State
  8. 量化交易——传统技术分析能量潮指标OBV的原理及实现
  9. 完整的京东联盟自定义推广链接生产程序
  10. 以太坊 智能合约 示例