查看: 863|回复: 0
收起左侧

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

[复制链接]

  离线 

  • TA的每日心情
    奋斗
    2021-3-3 12:32
  • 签到天数: 10 天

    [LV.3]

    发表于 2020-8-23 20:57:26 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 皋陶 于 2020-8-26 15:33 编辑

    文章目录


      • 前言
      • 关于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定时器:


    1. /* Use LIPT0 channel 0 for systick. */
    2. #define SYSTICK_LPIT LPIT0
    3. #define SYSTICK_LPIT_CH 0
    4. #define SYSTICK_LPIT_IRQn LPIT0_IRQn
    复制代码


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


    1. #define SysTick_Handler LPIT0_IRQHandler
    复制代码


    system_RV32M1_zero_riscy.c文件中的SysTick定时:


    1. /* Use LIPT1 channel 0 for systick. */
    2. #define SYSTICK_LPIT LPIT1
    3. #define SYSTICK_LPIT_CH 0
    4. #define SYSTICK_LPIT_IRQn LPIT1_IRQn
    复制代码


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


    1. /
    2. #define SysTick_Handler LPIT1_IRQHandler
    复制代码


    关于LPIT0

    LPIT0的每个通道都包含一个32位的计数器,加载计数值之后开始倒数,当倒数到0时,中断标志位被置1,通过向中断标志位写1来清除中断。


    为了尽量减少执行函数所消耗的时间,delay延时函数的采用了直接操作寄存器的方式来实现。


    通过阅读RV32M1参考手册【Chapter 50 Low Power Interrupt Timer (LPIT)】章节,和fsl_lpit.h库函数的实现,我们可以了解到以下几个寄存器的功能:


    寄存器名称全称读/写含义
    TVALTimer Value Register读/写设置定时器初值
    CVALCurrent Timer Value只读获取当前计数值
    SETENSet Timer Enable Register读写定时器使能控制
    CLRTENClear Timer Enable Register只写清除计数值
    MCRModule Control Register读写定时器时钟使能控制
    MSRModule 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文件

    1. #include "delay.h"

    2. /*
    3. * ZERO : LPIT1_CH0
    4. * RI5CY: LPIT0_CH0
    5. * */

    6. static uint8_t  fac_us=0;
    7. static uint16_t fac_ms=0;

    8. #if defined(CPU_RV32M1_zero_riscy)

    9. /*
    10. * RISC_V ZERO 使用 LPIT1_CH0作为SysTick,与RI5CY不同
    11. * */

    12. void Delay_Init(void)
    13. {
    14.         CLOCK_SetIpSrc(kCLOCK_Lpit1, kCLOCK_IpSrcFircAsync);        //设置定时器时钟48MHz
    15.         LOG("LPIT1时钟: %ld \r\n", CLOCK_GetIpFreq(kCLOCK_Lpit1));        //输出LPIT0时钟

    16.         CLOCK_EnableClock(kCLOCK_Lpit1);        //使能时钟
    17.         LPIT_Reset(LPIT1);                                        //复位定时器
    18.         LPIT1->MCR = LPIT_MCR_M_CEN_MASK;        //使能定时器

    19.         fac_us = CLOCK_GetIpFreq(kCLOCK_Lpit1)/1000000;
    20.         fac_ms = fac_us*1000;
    21. }

    22. void Delay_us(uint32_t Nus)
    23. {
    24.         LPIT1->CHANNEL[kLPIT_Chnl_0].TVAL =  48 * Nus - 1;                                        //加载时间
    25.         LPIT1->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0);                //启动定时器
    26.         while(LPIT1->CHANNEL[kLPIT_Chnl_0].CVAL);                                                        //等待计数值到0
    27. //        while((LPIT1->MSR & 0x0001) != 0x01);                                                                //等待溢出
    28. //        LPIT0->MSR |= (1U << kLPIT_Chnl_0);                                                                        //写1,清除中断
    29.         LPIT1->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0);                //清除计数器
    30. }

    31. void Delay_ms(uint32_t Nms)
    32. {
    33.         LPIT1->CHANNEL[kLPIT_Chnl_0].TVAL = Nms * fac_ms  - 1;                        //加载时间
    34.         LPIT1->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0);        //启动定时器
    35.         while(LPIT1->CHANNEL[kLPIT_Chnl_0].CVAL);                                                //等待计数到0
    36. //        while((LPIT1->MSR & 0x0001) != 0x0001);                                                        //等待产生中断
    37. //        LPIT0->MSR |= (1U << kLPIT_Chnl_0);                                                                //向中断标志位写1,清除中断
    38.         LPIT1->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0);        //清除计数器
    39. }

    40. #elif defined(CPU_RV32M1_ri5cy)

    41. /*
    42. * RISC_V RI5CY 使用 LPIT0_CH0作为SysTick,与ZERO不同
    43. * */

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

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

    51.         fac_us = CLOCK_GetIpFreq(kCLOCK_Lpit0)/1000000;
    52.         fac_ms = fac_us*1000;
    53. }

    54. void Delay_us(uint32_t Nus)
    55. {
    56.         LPIT0->CHANNEL[kLPIT_Chnl_0].TVAL =  48 * Nus - 1;                                        //加载时间
    57.         LPIT0->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0);                //启动定时器
    58.         while(LPIT0->CHANNEL[kLPIT_Chnl_0].CVAL);                                                        //等待计数值到0
    59. //        while((LPIT0->MSR & 0x0001) != 0x01);                                                                //等待溢出
    60. //        LPIT0->MSR |= (1U << kLPIT_Chnl_0);                                                                        //写1,清除中断
    61.         LPIT0->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0);                //清除计数器
    62. }

    63. void Delay_ms(uint32_t Nms)
    64. {
    65.         LPIT0->CHANNEL[kLPIT_Chnl_0].TVAL = Nms * fac_ms  - 1;                        //加载时间
    66.         LPIT0->SETTEN |= (LPIT_SETTEN_SET_T_EN_0_MASK << kLPIT_Chnl_0);        //启动定时器
    67.         while(LPIT0->CHANNEL[kLPIT_Chnl_0].CVAL);                                                //等待计数到0
    68. //        while((LPIT0->MSR & 0x0001) != 0x0001);                                                        //等待产生中断
    69. //        LPIT0->MSR |= (1U << kLPIT_Chnl_0);                                                                //向中断标志位写1,清除中断
    70.         LPIT0->CLRTEN |= (LPIT_CLRTEN_CLR_T_EN_0_MASK << kLPIT_Chnl_0);        //清除计数器
    71. }

    72. #endif
    复制代码

    delay.h文件

    1. #ifndef __DELAY_H__
    2. #define __DELAY_H__

    3. #include "fsl_lpit.h"
    4. #include "fsl_debug_console.h"
    5. #include "sys.h"

    6. /*
    7. * ZERO : LPIT1_CH0
    8. * RI5CY: LPIT0_CH0
    9. * */

    10. void Delay_Init(void);
    11. void Delay_ms(uint32_t Nms);
    12. void Delay_us(uint32_t Nus);

    13. #endif
    复制代码

    实际验证

    1. ...

    2. #include "delay.h"
    3. ...

    4. int main(void)
    5. {
    6.     ...
    7.     Delay_Init();
    8.     ...
    9.     while(1)
    10.     {
    11.         GPIOA->PTOR = 1 << 24;        //寄存器方式操作,减小误差
    12.         Delay_ms(100);        //延时微秒函数验证
    13. //          Delay_us(5);        //延时微秒函数验证              
    14.     }
    15. }
    复制代码


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


    驱动IIC接口OLED
    • 社区首页的LOGO图片

    国内芯片技术交流-织女星开发板RISC-V内核实现微秒级精确延时risc-v单片机中文社区(1)

    • OLED实际显示效果:

    国内芯片技术交流-织女星开发板RISC-V内核实现微秒级精确延时risc-v单片机中文社区(2)


    总结

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


    参考资料历史精选本篇完,感谢关注:RISC-V单片机中文网




    上一篇:picoRV32 (RISC-V) GCC 编译环境
    下一篇:Design of the RISC-V Instruction Set Architecture笔记(chapter1-2)
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-4-24 22:22 , Processed in 0.744127 second(s), 48 queries .

    快速回复 返回顶部 返回列表