首页 > 其他分享 >RISCV学习(4)GD32VF103 MCU芯片学习

RISCV学习(4)GD32VF103 MCU芯片学习

时间:2025-01-13 09:32:16浏览次数:3  
标签:OFF set LED RISCV GD32VF103 中断 GPIO MCU eclic

笔者有空学习了GD32的RSICV芯片,故来总结一下。

GD32 RISCV芯片系列

GD:GidaDeivce,兆易创新,生产的MCU的内核架构系列如下图所述,主要是ARM架构的,Cortex-M23、M3、M4、M33以及M7,然后也涉及到了RISC-V架构的,笔者今天就来聊一下RISCV架构的MCU产品。
在这里插入图片描述
GD32的RISC-V的芯片类型主要有以下几种:
在这里插入图片描述
特点如下:
GD32VF103系列MCU采用了全新的基于开源指令集架构RISC-V的Bumblebee处理器内核,是兆易创新(Gigadevice)携手中国领先的RISC-V处理器内核IP和解决方案厂商芯来科技(Nuclei System Technology),面向物联网及其它超低功耗场景应用自主联合开发的一款商用RISC-V处理器内核。

  • GD32VF103系列RISC-V MCU提供了108MHz的运算主频,
  • 16KB到128KB的片上闪存和6KB到32KB的SRAM缓存,
  • gFlash®专利技术支持内核访问闪存高速零等待。
  • Bumblebee内核还内置了单周期硬件乘法器、硬件除法器和加速单元应对高级运算和数据处理的挑战。

GD32 RISCV VF103芯片

在这里插入图片描述

来具体看一下VF103 芯片的一些特点:RISC-V处理器适用于低能耗、小面积的嵌入式应用,具有简单的动态分支预测、指令预取缓冲区 和 本 地 内 存 等 多 种 高 效 微 架 构 特 点 。它支持32个通用寄存器(GPRs)和用于性能/面积权衡的快速乘法器:

  •  RISC-V兼容小端RV32 IMAC(32 GPRS) ;
  •  可配置的2级管道,针对低门数和高频率进行了优化;
  • 机器(M)和用户(U)权限级别支持;
  •  支持单周期硬件乘法器和多周期硬件除法器;
  •  硬件支持加载/存储未对齐;
  •  硬件支持原子指令;
  •  支持不可屏蔽中断(NMI) ;
  •  动态分支预测和指令预取缓冲器用于加速控制代码;
  •  目前最先进的微架构设计以权衡面积和性能要求;
  •  支持WFI(等待中断) ;
  •  WFE(等待事件)支持;
  •  中断优先级可配置/可编程;
  •  适用于实时性能的增强矢量中断处理;
  • 支持中断优先抢占;
  •  支持咬尾中断;
  •  标准4线JTAG调试端口;
  •  支持交互式调试功能;
  •  支持4个硬件断点触发器
    具体架构如下图:
    在这里插入图片描述
    ICode和DCode用来访问Flash的数据,系统总线通过AHB矩阵访问,SRAM、AHB、APB1和APB2总线。
  • DMA可以通过AHB矩阵访问memory,可以做内存拷贝。
  • AHB挂载一些高速外设:FMC、USB_FS、CRC以及RCU
  • APB2挂载一些次高速外设:GPIO、USART0,SPI0、Timer0,EXIT以及ADC0和1
  • APB1挂载一些低俗外设:USART1和2,UART2和UART3,CAN0/1、RTC、TIMER1-6等等
  • ECLIC:中断控制器,外设的中断可以到这里。

memory map位置:与GD32其他系列类似:

  • Flash位置:0x08000000
  • SRAM位置:0x20000000
  • 外设地址:0x40000000开始
    在这里插入图片描述
    在这里插入图片描述
    启动boot设置也一样:
    在这里插入图片描述
    接着有两个地址可以读一些Flash以及Device id的信息。
  • 0x1FFF F7E0:Flash以及SRAM大小
  • 0x1FFF F7E8:device ID信息(96位)
    在这里插入图片描述
    Flash通过FMC去操作,从上图的架构图也可以看出,主要写和擦,读可以直接通过memory map的方式,以1KB为一个page。
    在这里插入图片描述
    接着介绍一下时钟:与GD32其他芯片类似,与STM32的时钟图也类似,这列不多介绍。
    在这里插入图片描述

GD32 RISCV VF103中断机制

这个中断机制属于芯片架构内部的部分,需要介绍一下。采用ECLIC中断模式,改进型内核中断控制器(Enhanced Core Local Interrupt
Controller, ECLIC)。

中断类型:

其支持外部中断和内部中断。

  • 外部中断主要是外设比如UART、Timer的一些中断。
  • 内部中断主要是软件中断以及定时器中断。
    在这里插入图片描述

中断屏蔽、中断级别、 优先级与仲裁

根据中断级别(Level)和中断优先级(Priority)进行确定。,两种级别的位数可以通过先关的寄存器确定。
在这里插入图片描述
在这里插入图片描述

  • 中断级别(level)决定了中断是否可以嵌套
  • 中断优先级(priority)以及中断ID 会进行中断裁决,不参与中断嵌套的抉择
  • 中断阈值也可以设置。裁决出的中断高于阈值才会被CPU响应。
  • 前提条件是,中断被使能,且中断已经产生了,才可以参与裁决。

中断ID编码如下:
在这里插入图片描述
在这里插入图片描述

中断处理模式

支持向量模式以及非向量模式。

  • 向量模式:中断向量表,CPU根据中断ID和偏移直接 去中断向量基地址去拿地址,然后跳到中断地址(即中断函数)去执行。主要需要加关键字__attribute__((interrupt))),编译器可以加一些上下文保护。
  • 非向量模式:即不是直接跳到中断函数地址,而是跳到统一的函数入口,然后再跳到对应的中断函数。

向量模式下的处理流程:
在这里插入图片描述
如果需要处理中断嵌套,则需要在中断开始加入处理,因为进入中断前,全局中断已经关闭。

在这里插入图片描述
中断向量模式的设置,通过clicintattr[i]的比特域
在这里插入图片描述

//sets vector-mode or non-vector mode 
void eclic_set_vmode(unsigned int source) {
  //read the current attr 
  unsigned char old_intattr = eclic_get_intattr(source);
      // Keep other bits unchanged and only set the LSB bit
  unsigned char new_intattr = (old_intattr | 0x1); 

  eclic_set_intattr(source,new_intattr);
}

void eclic_set_nonvmode(unsigned int source) {
  //read the current attr 
  unsigned char old_intattr = eclic_get_intattr(source);
      // Keep other bits unchanged and only clear the LSB bit
  unsigned char new_intattr = (old_intattr & (~0x1));

  eclic_set_intattr(source,new_intattr);
}
  • mepc:保存进入异常之前,处理器正在执行的PC值。
  • mcause:进入中断异常之前原因。
  • mtval:进入异常之前的出错编码值 或者存储器的地址
  • msubm:内核自定义寄存器,进入异常之前的异常类型。
  • mtvec:异常处理入口,低6位,作为中断模式的选择
  • mtvt:中断向量基地址

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
非向量模式:

  •  mtvt2 如果最低位为0,则和异常共用一个函数入口
  •  最低位如果为1,则通过mtvt2 指定单独的中断函数入口
  •  公共函数入口,首先保存 CSR 寄存器 mepc、 mcause、 msubm 入堆栈。保存这几个 CSR 寄存器是为了保证后续的中断嵌套能够功能正确,因为新的中断响应会重新覆盖 mepc、 mcause、 msubm的值,因此需要将它们先保存入堆栈。
  •  保存若干通用寄存器(处理器的上下文)入堆栈。
  •  然后执行一条特殊的指令“csrrw ra, CSR_JALMNXTI, ra”。如果没有中断在等待(Pending),则该指令相当于是个 Nop 指令不做任何操作;如果有中断在等待(Pending),执行该指令后处理器会:
  • 直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序(Interrupt Service Routine, ISR)中去。
  •  在跳入中断服务程序的同时,硬件也会同时打开中断的全局使能,即,设置 mstatus寄存器的 MIE 域为 1。打开中断全局使能后,新的中断便可以被响应,从而达到中断嵌套的效果。
  •  在跳入中断服务程序的同时,“csrrw ra, CSR_JALMNXTI, ra”指令还会达到 JAL(Jump and Link)的效果,硬件同时更新 Link 寄存器的值为该指令的 PC 自身作为函数调用的返回地址。因此,从中断服务程序函数返回后会回到该“csrrw ra,CSR_JALMNXTI, ra”指令重新执行,重新判断是否还有中断在等待(Pending),从而达到中断咬尾的效果。
  • 在中断服务程序的结尾处同样需要添加对应的恢复上下文出栈操作。并且在 CSR 寄存器 mepc、mcause、msubm 出堆栈之前,需要将中断全局使能再次关闭,以保证 mepc、mcause、 msubm 恢复操作的原子性(不被新的中断所打断)
    在这里插入图片描述
    中断咬尾:背靠背的恢复现场和保存现场。
    在这里插入图片描述

GD32 RISCV VF103芯片例程学习

流水灯


#define LED_RED_ON()     gpio_bit_reset(GPIOC, GPIO_PIN_13)
#define LED_BLUE_ON()    gpio_bit_reset(GPIOA, GPIO_PIN_2)
#define LED_GREEN_ON()   gpio_bit_reset(GPIOA, GPIO_PIN_1)


#define LED_RED_OFF()     gpio_bit_set(GPIOC, GPIO_PIN_13)
#define LED_GREEN_OFF()   gpio_bit_set(GPIOA, GPIO_PIN_1)
#define LED_BLUE_OFF()    gpio_bit_set(GPIOA, GPIO_PIN_2)

void GPIO_Init(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);   //打开GPIOA时钟
    rcu_periph_clock_enable(RCU_GPIOC);   //打开GPIOA时钟
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_1);
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_2);
    gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_13);
    gpio_bit_set(GPIOA, GPIO_PIN_1);
    gpio_bit_set(GPIOA, GPIO_PIN_2);
    gpio_bit_set(GPIOC, GPIO_PIN_13);
}

void led_blink(unsigned int counter)
{
    unsigned int blink_state_num = counter%8;
    switch(blink_state_num)
    {
        case 0:
        {
            LED_RED_ON();
            LED_GREEN_OFF();
            LED_BLUE_OFF();
        }break;
        case 1:
        {
            LED_RED_ON();
            LED_BLUE_ON();
            LED_GREEN_ON();
        }break;
        case 2:
        {
            LED_RED_ON();
            LED_GREEN_ON();
            LED_BLUE_OFF();
        }break;
        case 3:
        {
            LED_RED_OFF();
            LED_BLUE_OFF();
            LED_GREEN_ON();
        }break;
        case 4:
        {
            LED_RED_OFF();
            LED_GREEN_ON();
            LED_BLUE_ON();
        }break;
        case 5:
        {
            LED_RED_OFF();
            LED_GREEN_OFF();
            LED_BLUE_ON();
        }break;
        case 6:
        {
            LED_RED_ON();
            LED_BLUE_ON();
            LED_GREEN_OFF();
        }break;
        case 7:
        {
            LED_RED_OFF();
            LED_GREEN_OFF();
            LED_BLUE_OFF();
        }break;
        default:
        break;

    }
}

串口发送

这里注意串口发送函数,官方驱动有点问题,发送完了需要等待发送完成,不然就会发送不全的情况产生

void usart_data_transmit(uint32_t usart_periph, uint32_t data)
{
    while((USART_STAT(usart_periph) & USART_STAT_TC) == 0);  //注意等待完成完成
    USART_DATA(usart_periph) = USART_DATA_DATA & data;
}
void UART0_Init(void)
{
    rcu_periph_clock_enable(RCU_USART0);  //打开USART0时钟
    rcu_periph_clock_enable(RCU_GPIOA);   //打开GPIOA时钟
    //TX  PA9
    //RX  PA10
    gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_10MHZ,GPIO_PIN_9);  //设置GPIOA9为服用输出模式  
    gpio_init(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_10MHZ,GPIO_PIN_10);  //设置GPIOA10为浮空输入模式

    usart_deinit(USART0);   //复位USART0
    rcu_periph_clock_enable(RCU_AF);  //使能复用时钟
    usart_baudrate_set(USART0,115200);  //设置波特率为115200
    usart_parity_config(USART0,USART_PM_NONE);  //设置校验位为无
    usart_word_length_set(USART0,USART_WL_8BIT);  //设置传输长度8Bit
    usart_stop_bit_set(USART0,USART_STB_1BIT);  //设置停止位1位
    usart_transmit_config(USART0,USART_TRANSMIT_ENABLE);  //设置传输使能
    usart_enable(USART0);    //开启UART0
}
 void test_printf(const char* fmt, ...)
 {
    char buf[256]={0};
    va_list ap;
    va_start(ap,fmt);
    int len = vsnprintf(buf,(unsigned int)256,fmt,ap);
    if(len)
    {
        int i;
        for(i=0;i<len;i++)
        {
            usart_data_transmit(USART0,buf[i]);
        }   
    }
    va_end(ap);
 }

向量中断与非向量模式

向量模式下需要:eclic_set_vmode(TIMER3_IRQn); 设置相应的函数为向量模式

    /* enable the global interrupt */
    eclic_global_interrupt_enable();
    eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
    /* enable and set key EXTI interrupt priority */
#if (INTERRUPT_MODE == INTERRUPT_VECTOR_MODE) 
    eclic_set_vmode(TIMER3_IRQn);
#else
    eclic_set_nonvmode(TIMER3_IRQn);
#endif
    eclic_set_posedge_trig(TIMER3_IRQn);
    eclic_irq_enable(TIMER3_IRQn, 1, 0);
    test_printf("eclic_get_mth=%d\r\n",eclic_get_mth());

int test_counter_g = 0;
#if (INTERRUPT_MODE==INTERRUPT_VECTOR_MODE)
 __attribute__((interrupt)) void TIMER3_IRQHandler()
#else
void TIMER3_IRQHandler()
#endif
{
    if(timer_interrupt_flag_get(TIMER3,TIMER_INT_FLAG_UP))
    {
        test_counter_g++;
    }  
    timer_interrupt_flag_clear(TIMER3,TIMER_INT_FLAG_UP); 
}

参考

1、基于RISC-V内核的32位通用微控制器(MCU)
2、Bumblebee内核指令架构手册
3、向量中断以及非向量中断代码例程–Github
4、向量中断以及非向量中断代码例程–CSDN

标签:OFF,set,LED,RISCV,GD32VF103,中断,GPIO,MCU,eclic
From: https://blog.csdn.net/qq_34430371/article/details/144747583

相关文章

  • 【STM32】MCU运行多段代码,Flash程序更新的实现方式之一
    【STM32】MCU运行多段代码,Flash程序更新的实现方式之一文章目录BootLeader跳转到BootLeader跳转到Flash其他位置MCU运行多段代码其他程序更新烧录方式附录:Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作SysTick系统定时器精准延时延时函数阻塞延时非阻塞延时......
  • CPU、MCU、MPU、SOC、DSP、ECU、GPU、FPGA傻傻分不清楚?一文讲清它们的区别
    前言在电子领域中,我们经常提到CPU、MCU、MPU、SOC、DSP、ECU、GPU、FPGA等,它们都是常见的芯片或处理器类型,但是却在结构、功能和应用场景上存在着显著的差异。有些朋友可能不太能区分,今天我们就来依次介绍一下。一、定义与功能1、CPU(CentralProcessingUnit,中央处理器)定义:是......
  • MCU命令
    三D打印常用MCU命令allocate_oidscount=42分配42个OIDs,后续用于标识和管理不同的设备或组件config_spioid=0pin=PD7cs_active_high=0配置一个spi对象,对象id是0,通过引脚PD7进行SPI通信,chipselect信号为低电平有效config_digital_outoid=15pin=PE1value=0d......
  • 在FreeBSD或Ubuntu平台仿真RISCV64位版本FreeBSD系统相关技术文档
    本文档主要是针对没有实体机,用FreeBSD或Ubuntu平台仿真FreeBSDRISCV64系统的技术实现。RISCV64介绍RISCV64是一种基于RISC-V(以后简称RISCV)指令集架构(ISA)的64位处理器设计。RISCV是一种开放的指令集架构,由加州大学伯克利分校的研究团队于2010年首次发布,其设计目标是提供一个......
  • 痞子衡嵌入式:MCUXpresso for VS Code开发环境搭建及SDK工程导入
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是MCUXpressoforVSCode开发环境搭建及SDK工程导入。MCUXpressoIDE(包括其前身LPCXpressoIDE、KinetisDesignStudio)是恩智浦软件团队持续开发了十多年的免费集成开发环境,现在功能已经相当完善,IDE里面......
  • 【硬件接口】MCU的IO模式
    本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时,也能帮助其他需要参考的朋友。如有谬误,欢迎大家进行指正。一、基本分类IO口分为GPIO(通用输入输出端口)口和专用IO口。其中,GPIO口具有高度的灵活性,可以根据需要配置为不同的工作模式。二、GPIO的工作模式1.浮......
  • MCU驱动使用
    一、时钟的配置:AG32通常使用HSE外部晶体(范围:4M~16M)。AG32中不需要手动设置PLL时钟(时钟树由系统自动配置,无须用户关注)。用户只需在配置文件中给出外部晶振频率和系统主频即可。配置方式:在ve文件中配置如下:这里配置的值,会在mcu的系统初始化时,代码中自动获取并使能......
  • 关于CH32系列MCU GPIO使用
    1、关于CH32V103PD0/PD1引脚使用PD0、PD1引脚为外部HSE晶振引脚,作为普通GPIO使用的时候注意:需要关闭外部晶振,开启复用时钟,使用HSI配置系统主频,否则无法正常运行。  2、关于CH32V003PA1/PA2引脚时用PA1、PA2引脚可以作为外部晶振引脚使用,注意若要作为普通GPIO使用时,需要......
  • 痞子衡嵌入式:MCUXpresso IDE下C++源文件中嵌套定义的复合数据类型命名空间认定
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是MCUXpressoIDE下C++源文件中嵌套定义的复合数据类型命名空间认定。痞子衡之前写过一篇文章《MCUXpressoIDE下添加C++源文件进SDK工程编译的方法》,通过这篇文章我们知道嵌入式工程里是能够支持C源文件......
  • freeswitch(开启支持MCU视频会议,使用mod_av模块)
    亲测版本centos7.9系统–》freeswitch1.10.9本人freeswitch安装路径(根据自己的路径进入)/usr/local/freeswitch/etc/freeswitch场景说明:有些场景想使用视频会议MCU融合画面进行开会使用方法:第一步:下载插件yuminstall-yepel-releaseyuminstall......