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

risc-v Sifive learn inventor基础之串口&操作寄存器 HifiveRev B

[复制链接]

  离线 

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

    [LV.3]

    发表于 2020-8-24 12:03:48 | 显示全部楼层 |阅读模式

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

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

    x
    本帖最后由 皋陶 于 2020-8-25 18:02 编辑

    isc-v Sifive learn inventor基础之串口
    本文目录


    上一章了解了中断后,继续实践另一个重要的外设串口以及Sifive提供的操作寄存器的函数__METAL_ACCESS_ONCE
    Sifive learn inventor基础之串口gpio中断配置


    开发板只有两个串口,分别是uart0和uart1,uart0外接到jlink模块,可以用于与上位机的通信,uart1与esp32连接。


    本章以uart0为例子来初始化uart0,并且实现pc端串口发送数据,开发板自动返回接收的数据。


    一,硬件连接

    由芯片手册可以知道,uart0_rx对应gpio16,uart0_tx对应gpio17;所以我们需要复用这两gpio口;


    二,代码编写

    1,初始化uart0

    1. /**
    2. * 串口0 波特率115200 用于打印数据
    3. */
    4. void uart0_init()
    5. {
    6.         //enable rx and tx
    7.         UART0_TXCTRL  |= (1 <<0);
    8.         UART0_RXCTRL  |= (1 <<0);
    9.         
    10.         //RXWM 1 watermark=0
    11.         //__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_RXCTRL))  |= (1 <<16);

    12.         //enable rx inturupt
    13.         UART0_IE |= (1 <<1);

    14.         //先将div寄存器清零,再进行赋值操作
    15.         UART0_DIV  &= 0;
    16.         //设置波特率为 115200 时钟频率64M,div=(64M/115200)-1=554
    17.         UART0_DIV |=554;
    18.         //复用gpio16,17
    19.         GPIO0_IOF_EN  |= (1 <<16);
    20.         GPIO0_IOF_EN  |= (1 <<17);

    21. }
    复制代码


    因为库函数用不惯(好多bug),所以自己通过操作寄存器初始化uart,首先要去看芯片手册的uart章节,了解各个寄存器的功能。


    学过stm32的人一看就清楚这是在配置寄存器,需要搭配芯片手册看才能了解每一步的意义。


    以下是在uart.h中对用到的uart寄存器的定义,有了这些宏定义,对寄存器的操作看起来就比较简洁。


    这里介绍一个非常重要的库函数__METAL_ACCESS_ONCE,可以看到这是一个宏定义函数,大概的意思就是操作地址为(x)的寄存器;


    具体寄存器地址在手册里可以找到,另外bsp/install/include/metal/machine目录下的platform.h文件里,定义了大部分寄存器的地址,使用起来就是复制粘贴,非常方便

    国内芯片技术交流-risc-v Sifive learn inventor基础之串口&操作寄存器 HifiveRev Brisc-v单片机中文社区(1)

    uart.h
    1. #define UART1_RXDATA (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_1_BASE_ADDRESS + METAL_SIFIVE_UART0_RXDATA)))
    2. #define UART0_RXDATA __METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_RXDATA))
    3. #define UART1_TXDATA (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_1_BASE_ADDRESS + METAL_SIFIVE_UART0_TXDATA)))
    4. #define UART0_TXDATA __METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_TXDATA))

    5. #define UART0_TXCTRL (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_TXCTRL)))
    6. #define UART0_RXCTRL (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_RXCTRL)))
    7. #define UART0_IE (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_IE)))
    8. #define UART0_DIV (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_DIV)))

    9. #define UART1_TXCTRL (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_1_BASE_ADDRESS + METAL_SIFIVE_UART0_TXCTRL)))
    10. #define UART1_RXCTRL (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_1_BASE_ADDRESS + METAL_SIFIVE_UART0_RXCTRL)))
    11. #define UART1_IE (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_1_BASE_ADDRESS + METAL_SIFIVE_UART0_IE)))
    12. #define UART1_DIV (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_1_BASE_ADDRESS + METAL_SIFIVE_UART0_DIV)))

    13. #define GPIO0_IOF_EN (__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_GPIO0_0_BASE_ADDRESS + METAL_SIFIVE_GPIO0_IOF_EN)))
    复制代码


    初始化函数中的被注释部分__METAL_ACCESS_ONCE((__metal_io_u32 *)(METAL_SIFIVE_UART0_0_BASE_ADDRESS + METAL_SIFIVE_UART0_RXCTRL)) |= (1 <<16);可能比较难以理解,结合芯片手册,得知操作的是watermark功能 。


    因为接收FIFO是8个字节长,当设置watermark=2时,只有当FIFO里的数据超过2个字节时,uart才会产生中断。



    当FIFO里的数据少于2时,中断标志位就会自动清除。所以watermark可以认为是一个门槛,超过门槛就会触发中断。我一般设置为默认,就是0;

    国内芯片技术交流-risc-v Sifive learn inventor基础之串口&操作寄存器 HifiveRev Brisc-v单片机中文社区(2)


    watermark官网手册说明
    国内芯片技术交流-risc-v Sifive learn inventor基础之串口&操作寄存器 HifiveRev Brisc-v单片机中文社区(3)


    2,配置plic
    就像上一章一样,配置plic中断,这一次就非常能理解了。

    1. /**
    2. *串口0接收中断初始化
    3. *uart0 uart0对象
    4. *flag 退出中断后的标志
    5. */
    6. void Uart0_rx_interrupt_init(struct metal_uart *uart0,int *flag)
    7. {
    8.         //uart0 id=33 查询手册
    9.         int  uart0_id=33;
    10.         uart0_intr=uart0->vtable->controller_interrupt(uart0);

    11.         metal_interrupt_init(uart0_intr);
    12.    
    13.         //注册回调函数 传递flag
    14.         metal_interrupt_register_handler(uart0_intr,uart0_id,uart0_isr,flag);
    15.         //设置优先级
    16.         metal_interrupt_set_priority(uart0_intr,uart0_id,4);
    17.         metal_interrupt_enable(uart0_intr,uart0_id);
    18.         
    19. }
    20. /**
    21. *串口0接收中断回调函数
    22. *每接收一个字节进入一次此函数 每次进入会读取一个字节数据到buff
    23. */
    24. void uart0_isr (int id, void *data) {
    25.         int *flag=(int *)data;
    26.         //读取uart0接收寄存器
    27.         uart0_buff.rxbyte=UART0_RXDATA;
    28.         //将读到的一个字节的数据放到buff
    29.         uart0_buff.rxdata[uart0_buff.rn]=uart0_buff.rxbyte&0x0ff;
    30.         uart0_buff.rn++;
    31.         //UART0_RXDATA寄存器的31位为1时表示FIFO里已经没有数据,说明接收完成
    32.         if((UART0_RXDATA>>31)&1){
    33.                 uart0_buff.rxdata[uart0_buff.rn]=0;
    34.                 *flag=3;
    35.         }
    36. }
    复制代码


    3,当uart0接收全部数据后,flag=3,再把数据发送回去

    1. /**
    2. * 串口0发送 用于打印
    3. * char *p 字符串的首地址
    4. * len 字符串长度(字节)
    5. */
    6. void uart0send(char *p,int len)
    7. {
    8.         //UART0_TXDATA的第31位为1时表示发送FIFO为空,即发送完成
    9.         for(int i=0;i<len;i++){
    10.                 while(UART0_TXDATA&(1<<31));
    11.                 //发送一个字节的数据
    12.                 UART0_TXDATA  |=p[i];

    13.         }
    14. }
    复制代码

    示例:

    1. if(flag==3){
    2.         char *c="uart0 recieve data:\r\n";
    3.         int len=strlen(c);
    4.         uartsend(c,len);
    5.         uart0send(uart0_buff.rxdata,uart0_buff.rn);
    6. }
    复制代码


    三,小结

    这个操作寄存器的函数非常有用,结合芯片手册,我们可以避开库函数,更好的去了解芯片的底层逻辑。对一些库函数没有涉及的外设,例如pwm也需要操作寄存器来进行开发。

    国内芯片技术交流-risc-v Sifive learn inventor基础之串口&操作寄存器 HifiveRev Brisc-v单片机中文社区(4)
    本篇完,感谢关注:RISC-V单片机中文网




    上一篇:清华大学陈渝副教授:尝试将Rust/Risc-V等新技术用于系统类
    下一篇:RISC-V 常见指令
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

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



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

    GMT+8, 2024-4-23 20:23 , Processed in 0.726089 second(s), 48 queries .

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