沁恒 CH32V103 MCU介绍及呼吸灯演示

一. 沁恒微电子

沁恒 CH32V103 MCU介绍及呼吸灯演示

南京沁恒微电子股份有限公司是一家高速数模混合公司主要产品集成电路设计公司,成立于2004年,公司位于江苏南京。公司主要在物联网领域专注于连接和控制方面的芯片设计以及应用技术开发。

沁恒微电子于2020年2月24日发布了首款基于 RISC-V架构,自主设计的RISC-V3A处理器内核及 其硬件产品—— CH32V103系列MCU。

二. CH32V103系列MCU

沁恒 CH32V103 MCU介绍及呼吸灯演示

1.芯片简介

CH32V103 系列是以RISC-V3A处理器为核心的32位通用微控制器,该处理器是基于RISC-V开源指令集设计。片上集成了时钟安全机制、多级电源管理、通用DMA控制器。此系列具有1路USB2.0主机/设备接口、多通道12位ADC转换模块、多通道触摸按键电容检测、多组定时器、多路I2C/USART/SPI接口等丰富的外设资源。

2. CH32V103x 产品型号说明

沁恒 CH32V103 MCU介绍及呼吸灯演示
1)芯片系列:CH32 代表的是沁恒微电子品牌的 32 位 MCU
2)芯片类型:V – RISC-V 内核,F – Cortex-M3 系列内核
3)芯片子系列:103 – 增强型
4)引脚数目:T – 36 脚,C – 48 脚,R – 64 脚,V – 100 脚,Z – 144 脚 5)Flash 容量:6 – 32K 字节 Flash,8 – 64K 字节 Flash,B – 128K 字节 Flash,C – 256K字节 Flash
6)封装信息:H – BGA 封装,T – LQFP 封装,U – VFQFPN 封装,Y – WLCSP/ WLCSP64
7)工作温度范围:6 – -40 ~ 85℃(工业级),7 – -40 ~ 105℃(工业级)

3. CH32V103x 产品型号对比

沁恒 CH32V103 MCU介绍及呼吸灯演示

4.产品内部架构框图

沁恒 CH32V103 MCU介绍及呼吸灯演示

5. 时钟树框图

沁恒 CH32V103 MCU介绍及呼吸灯演示

三. 沁恒 CH32V103R8T6 MCU

1. 产品特性

内核:
----32位RISC-V内核
----最高主频80MHz
----单周期乘法和硬件除法

低功耗:
多种低功耗模式(睡眠/停止/待机)

系统时钟:
----内嵌8MHz的RC振荡器
----内嵌40KHz的RC振荡器
----内嵌锁相环,最高120MHz
----外部支持32768Hz低速振荡器

存储器:
20KB易失数据存储区SRAM
64KB用户应用程序存储区CodeFlash

2. 外设模块

DMA控制器:
----7通道,支持环形缓冲区管理
----支持多外设

定时器:
1个实时时钟
1个高级定时器
1个滴答定时器
2个看门狗定时器
3个通用定时器

ADC:
1个12位逐次逼近型ADC
支持16个外部通道和2个内部通道

UASRT:
3个通用同步异步收发器,最高支持4.5Mbps

SPI:
2个串行外设接口,支持主、从、多从模式

USB2.0全速:
1个USB全速主机/设备控制器,支持USB2.0全速
12Mbps或低速1.5Mbps

沁恒 CH32V103 MCU介绍及呼吸灯演示

3. 应用案例

点灯测试是每一款MCU的必经之路,这里采用呼吸灯作为演示。

3.1 GPIO初始化

注意
由于演示程序采用呼吸灯,即通过改变LED对应IO的PWM占空比控制小灯亮灭程度,所以需要初始化具有PWM输出功能的引脚。

void GPIO_init(PWMCH_enum pwmch, uint32 freq, uint32 duty)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    uint16 match_temp;                                          //占空比值
    uint16 period_temp;                                         //周期值
    uint16 freq_div = 0;                                        //分频值

    pwm_gpio_init(pwmch);                                       //PWM引脚初始化

    if((pwmch >> 8) == 0x00)
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);    //使能定时器1时钟
    else if((pwmch >> 8) == 0x01)
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    //使能定时器2时钟
    else if((pwmch >> 8) == 0x02)
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    //使能定时器3时钟
    else if((pwmch >> 8) == 0x03)
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);    //使能定时器4时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);        //使能AFIO复用功能模块时钟

    //引脚重映射
    if((pwmch >> 4) == 0x10)        //PWM1的部分重映射
        GPIO_PinRemapConfig(GPIO_PartialRemap_TIM1, ENABLE);
    else if((pwmch >> 4) == 0x11)   //PWM2的完全重映射
        GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
    else if((pwmch >> 4) == 0x21)   //PWM3的完全重映射
        GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);
    else if((pwmch >> 4) == 0x22)   //PWM3的部分重映射
        GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

    //获取系统主频
    sys_clk = 8000000 * (((RCC->CFGR0 >> 18)&0x0F) + 2);

    freq_div = (uint16)((sys_clk / freq) >> 16);                            //多少分频
    period_temp = (uint16)(sys_clk/(freq*(freq_div + 1)));                  //周期
    match_temp = period_temp * duty / PWM_DUTY_MAX;                         //占空比

    //初始化TIM1-4
    TIM_TimeBaseStructure.TIM_Period = period_temp - 1;                     //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler = freq_div;                         //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;             //TIM向上计数模式
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    //初始化TIM1-4 PWM模式
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;                       //选择定时器模式:TIM脉冲宽度调制模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;           //比较输出使能
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputState_Disable;
    TIM_OCInitStructure.TIM_Pulse = match_temp;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;                //输出极性:TIM输出比较极性高
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_Low;               //输出极性:TIM输出比较极性高
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;



    TIM_TimeBaseInit(((TIM_TypeDef *) TIMERN[pwmch>>8]), &TIM_TimeBaseStructure);  //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


    if((pwmch & 0x03) == 0x00)                                          //通道选择
    {
        TIM_OC1Init(((TIM_TypeDef *) TIMERN[pwmch>>8]), &TIM_OCInitStructure );                       //定时器通道1初始化
        TIM_OC1PreloadConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OCPreload_Enable);               //定时器预装载配置
        TIM_OC1FastConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OC1FE);                             //比较捕获通道快速使能
    }
    else if((pwmch & 0x03) == 0x01)
    {
        TIM_OC2Init(((TIM_TypeDef *) TIMERN[pwmch>>8]), &TIM_OCInitStructure );
        TIM_OC2PreloadConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OCPreload_Enable);
        TIM_OC2FastConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OC2FE);                             //比较捕获通道快速使能
    }
    else if((pwmch & 0x03) == 0x02)
    {
        TIM_OC3Init(((TIM_TypeDef *) TIMERN[pwmch>>8]), &TIM_OCInitStructure );
        TIM_OC3PreloadConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OCPreload_Enable);
        TIM_OC3FastConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OC3FE);                             //比较捕获通道快速使能
    }
    else if((pwmch & 0x03) == 0x03)
    {
        TIM_OC4Init(((TIM_TypeDef *) TIMERN[pwmch>>8]), &TIM_OCInitStructure );
        TIM_OC4PreloadConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OCPreload_Enable);
        TIM_OC4FastConfig(((TIM_TypeDef *) TIMERN[pwmch>>8]), TIM_OC4FE);                             //比较捕获通道快速使能
    }

    TIM_Cmd(((TIM_TypeDef *) TIMERN[pwmch>>8]), ENABLE);                                              //定时器使能
    TIM_CtrlPWMOutputs(((TIM_TypeDef *) TIMERN[pwmch>>8]), ENABLE );
    //TIM_ARRPreloadConfig( TIM1, ENABLE );
}

3.2 逻辑功能介绍

呼吸灯实现亮灭变化的效果,在程序上就是PWM的成比例增加或减少。

在演示程序中,设置满占空比为10000,PWM频率为100,引脚占空比初值为10000,每次自减量为100。


    pwm_init(PWM4_CH3_B8,100,10000);
    pwm_init(PWM4_CH4_B9,100,10000);

 while(1)
    {
       systick_delay_ms(15);
       //程序运行时间为us级,为了使LED变化效果肉眼可见,这里使用延时函数,也可采用定时器
        if(T1 == 0)
        {
            T-=100;
            if(T == 0)
                T1 = 1;
        }
        else
        {
            T+=100;
            if(T == 10000)
            {
                T1 = 0;
            }
        }

        pwm_duty(PWM4_CH3_B8, 10000-T);
        pwm_duty(PWM4_CH4_B9, T);
}

3.3 演示视频

由于评估板板载LED未连接引脚,这里使用杜邦线将具有PWM输出功能的PB8,PB9与LED端口连接。

呼吸灯演示视频

从视频中可以看到,两颗LED灯循环实现呼吸效果。

3.4 示例程序

演示程序已经打包上传,但是一直在审核,审核通过后会将链接发在评论区。

上一篇:血氧仪MCU解决方案


下一篇:一个合格的嵌入式软件开发工程师要掌握哪些知识?