织女星开发板RISC-V内核实现微秒级精确延时
文章目录
- 前言
- 关于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内核实现微秒级精确延时相关推荐
- 织女星开发板使用RISC-V核驱动GPIO
文章目录 前言 准备工作 寄存器简介 GPIO配置PCR寄存器 GPIO控制寄存器 库函数简介 PORT_SetPinConfig PORT_SetPinMux GPIO_PinInit GPIO_W ...
- 真正的RISC-V开发板——VEGA织女星开发板开箱评测
文章目录 前言 关于RISC-V架构 关于VEGA织女星开发板 基于FPGA实现的RISC-V开发板 基于RISC-V芯片实现的开发板 开箱爆照 板载资源简介 主控芯片RV32M1简介 支持的开发工具 ...
- NXP恩智浦VEGA织女星开发板免费申请!
前言 大概两周前申请了一块NXP恩智浦的开发板,今天终于收到了!在这里推荐给大家,官方网站刚上线一个月左右,目前申请的人还不算多,感兴趣的朋友可以申请一个,体验一下这个四核性能怪兽.大厂就是大气,包装 ...
- 织女星开发板能移植linux吗,织女星开发板启动模式修改——从ARM M4核启动
前言 刚开始玩织女星开发板的时候,想先从熟悉的ARM核入手,连上Jlink,打开MDK版本的Demo程序,编译OK,却检测不到芯片,仔细看了一下文档,原来RV32M1芯片默认从RISC-V核启动,如果 ...
- 织女星开发板调试器升级为Jlink固件
文章目录 前言 准备工作 升级操作 升级Jlink驱动 板载接口的说明 历史精选 前言 为了能使用板载的FreeLink调试器来调试RISC-V内核,我们需要把默认的CMSIC-DAP固件,升级为JL ...
- mssql 无法启动调试器 数据为空_织女星开发板启动模式修改——从ARM M4核启动
织女星开发板启动模式修改--从ARM M4核启动 前言 刚开始玩织女星开发板的时候,想先从熟悉的ARM核入手,连上Jlink,打开MDK版本的Demo程序,编译OK,却检测不到芯片,仔细看了一下文档, ...
- 如何将RT-Thread移植到织女星开发板?
上个学期天津大学的吕卫老师,在他<微处理器系统>课程的实验环节上,让研究生们使用织女星开发板做了一些项目. 本文是其中一份工作,由国际工程师学院,电子与通信工程的徐扬扬和张千依同学共同完成 ...
- 将linux内核烧进arm板,ARM开发板上uClinux内核移植
<ARM开发板上uClinux内核移植>由会员分享,可在线阅读,更多相关<ARM开发板上uClinux内核移植(19页珍藏版)>请在人人文库网上搜索. 1.纷傲掌秀悸篷益哑檀扬 ...
- 织女星开发板RISC-V核通过SPI协议驱动ARDUINO LCD模块(显示)
前言 第一次写这个博客,算是新手吧,刚好有这个机会,手边有VEGA的开发板和Arduino的LCD模块,做了点小东西,想和大家分享一下. 一开始只是想着通过SPI协议初始化LCD屏幕,然后发个字符串就 ...
最新文章
- mysql主从 1050错误
- 《PHP精粹:编写高效PHP代码》——第1章面向对象编程
- C++中map的使用
- python-二级补充-idle的了解
- 为什么要返回function_宇宙飞船返回时,为什么要冒着烧毁的风险加速穿过大气层?...
- golang中container/list包中的坑
- 大数据之-hadoop知识体系架构---大数据之hadoop工作笔记0001
- 微软将取缔 8 亿 Windows 10 用户的密码!
- 轻轻松松统计代码行数
- C#获取上传文件的扩展名
- 软件测试——测试用例设计方法
- 实现写邮箱html页面,用html写的简单的邮箱登陆界面
- 延安.居民家庭计算机普及率,2004~2014年家庭互联网普及率及电脑持有率
- 详解文本格式(Text)[第二天]
- 深度学习基础6(微分,偏导,梯度,链式法则)
- maximo跟java_Maximo7.5远程调用maximo的手动输入节点工作流
- 2018ICPC焦作站 B - Ultraman vs. Aodzilla and Bodzilla(贪心)
- java 视频压缩_Java 压缩视频(无需安装插件)
- android 如何让应用程序在全部应用程序列表里显示跟隐藏!
- 遗传算法解整数规划IntCon
热门文章
- centos下安装teamviewer
- 编译cc1:all warnings being treated as errors
- Windows10系统下MPI编程环境配置(超级详细)
- 小明最近迷上了化学,几乎天天在实验室做实验,但是很多实验生成的化学产物的相对分子质量令他很困惑,不知如何计算,请你编程帮他计算
- 网页设计经典色彩搭配
- error: previous declaration of '****' was here
- 14.State-理解原理即可、Flink中状态的自动管理、无状态计算和有状态计算、状态分类、Managed State Raw State\Keyed StateOperator State
- 量化交易——传统技术分析能量潮指标OBV的原理及实现
- 完整的京东联盟自定义推广链接生产程序
- 以太坊 智能合约 示例