沁恒CH32V307使用记录:GPIO与EXTI

2023-10-26

目的

GPIO是单片机最基础的功能,EXTI最常用的场景就是GPIO用于输入时使用。这篇文章将对CH32V307中相关内容进行说明。

本文使用沁恒官方的开发板 (CH32V307-EVT-R1沁恒RISC-V模块MCU赤兔评估板) 进行演示。

本文演示中需要用到开发板上的KEY和LED,默认只是引入接口到排针,并没有和芯片GPIO口相连,下文使用中需要手动用杜邦线连接。
在这里插入图片描述

GPIO(通用输入输出接口)

基础说明

在这里插入图片描述
CH32V307的GPIO和大部分单片机一样支持多种工作模式: 浮空输入 上拉输入 下拉输入 模拟输入 开漏输出 推挽输出 复用功能的输入和输出

复位后 ,GPIO口运行在初始状态,这时大多数IO口都是运行在浮空输入状态 ,但也有HSE等外设相关的引脚是运行在外设复用的功能上。

沁恒官方提供了库函数用于操作GPIO口,主要是 ch32v30x_gpio.hch32v30x_gpio.c 两个文件,前者中声明了提供给用户调用的函数以及相关的枚举和宏定义类型等。

下面只介绍些GPIO的基础使用,剩余的功能大多数是结合外设复用或是中断等进行的,会在包含在那些内中中进行介绍。需要详细了解也可以直接查看上面的库函数文件。

初始化

初始化GPIO只要配置需要使用的GPIO口和工作模式及其附加参数等,比如下面方式:

// 下面函数将初始化 PA0 为推挽输出模式
void GPIO_Toggle_INIT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0}; // GPIO后初始化结构体
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //初始化GPIOA时钟
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 使用 Pin0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输出切换频率 // 这个通常在满足需求的情况下越低越好,比如使用 GPIO_Speed_2MHz 
    
    GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIO口
}

上面代码中出现的 GPIOA GPIO_Pin_0 GPIO_Mode_Out_PP 等枚举和宏定义类型都可以在 ch32v30x_gpio.h 文件中找到,其中同一组的 GPIO_Pin 可以多个一起使用,比如下面这样:

// 同时使用 PIN 0、2、4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4;

输出

这里以推挽输出模式进行介绍,这里直接以点亮控制控制开发板上的LED进行测试,用杜邦线将 LED1LED2 分别与 PC0PC1 相连接,然后使用 MounRiverStudio 新建 CH32V307VC 项目,将 main.c 中代码替换成如下即可:

#include "debug.h"

int main(void)
{
	Delay_Init();

	// 以下初始化 GPIOC 的 PIN0 和 PIN1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

	while(1)
    {
	    GPIO_SetBits(GPIOC, GPIO_Pin_0 | GPIO_Pin_1); // PC0 和 PC1 输出高电平,根据电路这将会熄灭LED
	    Delay_Ms(1000);
	    GPIO_ResetBits(GPIOC, GPIO_Pin_0 | GPIO_Pin_1); // PC0 和 PC1 输出低电平,根据电路这将会点亮LED
	    Delay_Ms(500);
	}
}

编译程序下载到开发板就可以看到LED闪烁了。

输出相关操作库函数有下面一些:

// 指定IO口输出高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
// 指定IO口输出低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
// 写指定IO口的电平值,BitAction可选Bit_RESET(0)和Bit_SET(1)
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); 
// 写一组IO口的输出值
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

也可以使用调试器来查看GPIOC的输出寄存器状态了解输出情况:
在这里插入图片描述
上面的 OUTDR 就是输出数据寄存器了,该寄存器数据也可以通过下面库函数进行读取:

uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

输入与电平读取

这里以下拉输入模式进行介绍,用杜邦线将 KEYPB2 相连接, PB2 设置为浮空输入模式,根据电路连接,当按键松开时读取到的输入电平为高,按键被按下时读取到的输入电平为低。

修改 main.c 代码如下:

#include "debug.h"

int main(void)
{
	Delay_Init();
    USART_Printf_Init(115200);

	// 以下初始化 PB2 为浮空输入模式
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

	while(1)
    {
	    Delay_Ms(2000);
	    // 读取PB2输入电平值
	    printf("PB2 Input Data:%d\r\n", GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2));
	}
}

输入数据读取相关操作库函数有下面一些:

// 读取指定IO口电平值
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
// 读取一组IO口电平值
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

锁定机制

锁定机制可以锁定IO口的配置,在一些可靠性要求高的场合下比较有用。经过特定的一个写序列后,选定的 IO 引脚配置将被锁定,在下
一个复位前无法更改。使用库函数的话只需要使用下面函数即可:

void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

EXTI(外部中断)

基础说明

在这里插入图片描述
CH32V307有二十多条外部中断线,大部分都是给GPIO用的,这里也只对这个进行介绍,其它的中断线都是用于特定功能了,会在那些功能下进行介绍。

GPIO使用外部中断通常用于输入模式下,根据中断配置当外部电平上升或下降变化时会触发中断。

GPIO上的外部中断使用时需要处理的内容如下:

  • 初始化GPIO;
  • 初始化EXTI;
  • 中断都受到中断控制器控制(NVIC),所以相应用能也需要初始化;
  • 编写外部中断触发时的回调函数;

外部中断的库函数主要位于 ch32v30x_exti.hch32v30x_exti.c 两个文件,前者中声明了提供给用户调用的函数以及相关的枚举和宏定义类型等。

使用演示

这里使用PB2触发外部中断,电路上保持 KEYPB2 的连接。将 main.c 代码改为如下:

#include "debug.h"

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 将外部中断配置为分组二,这样中断父优先级和子优先级取值都为0~3
	Delay_Init();
    USART_Printf_Init(115200);

	// 以下初始化 PB2 为浮空输入模式
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); // 此处需要把复用时钟也打开
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

//    EXTI_ClearITPendingBit(EXTI_Line2); // 推荐在使能中断前先清除一次中断,防止意外发生

    // 以下初始化外部中断
    EXTI_InitTypeDef EXTI_InitStructure = {0};
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource2); // 配置外部中断源为 PB2
    EXTI_InitStructure.EXTI_Line = EXTI_Line2; // 外部中断线2
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 配置为外部中断
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 配置上升沿下降沿都触发中断
    EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能外部中断
    EXTI_Init(&EXTI_InitStructure);

    // 以下初始化中断控制器
    NVIC_InitTypeDef NVIC_InitStructure = {0};
    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; // 配置使用 EXTI2 通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 父优先级设置为1,数值越小优先级越高。父优先级高的中断会抢占优先级低的中断
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; // 子优先级设置为2,数值越小优先级越高,子优先级不会引起抢占
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
    NVIC_Init(&NVIC_InitStructure);

	while(1){}
}

// 下面是中断回调函数的声明,该函数名是在启动文件 startup_ch32v30x_D8C.S 中定义的
// 需要注意的是后面的 __attribute__((interrupt())); 是必须的这是 GCC For RISCV 对于中断的一种处理
// 另外也可以使用__attribute__((interrupt("WCH-Interrupt-fast"))); 这样可以使用沁恒RISCV的快速中断功能
void EXTI2_IRQHandler(void) __attribute__((interrupt()));

// 下面是中断回调函数的定义
void EXTI2_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line2)!=RESET) // 检查是否是中断线2的中断
  {
    printf("PB2 Input Data:%d\r\n", GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2));
    EXTI_ClearITPendingBit(EXTI_Line2); // 清除中断标志,这样才能触发下次中断
  }
}

这里特别需要注意的是这颗RISC-V内核的单片机使用时中断处理函数需要特殊的标识声明后才能正常使用,比如上面代码中使用 ( void EXTI2_IRQHandler(void) __attribute__((interrupt())); ) 方式进行声明,不然进一次中断后程序就跑飞了。

在这里插入图片描述

演示中读取到的数据异常是由于按键抖动引起的,实际使用中需要添加软件和硬件上的按键消抖功能。

总结

GPIO与EXTI虽然是使用频率比较高的功能,但总体使用比较简单,没有太多需要详细说明的地方。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

沁恒CH32V307使用记录:GPIO与EXTI 的相关文章

  • 人生苦短,我学python

    人生苦短我学python 一个python小白的入门之路 初识Python 刚开始学python是在今年七月 对python的第一印象就是短小而又精悍 变量无需定义即可使用 数不清的大大小小的库信手拈来 笔者以为 python的语法与mat
  • 华为OD机试真题-最长连续子序列-2023年OD统一考试(B卷)

    题目描述 有N个正整数组成的一个序列 给定整数sum 求长度最长的连续子序列 使他们的和等于sum 返回此子序列的长度 如果没有满足要求的序列 返回 1 输入描述 序列 1 2 3 4 2 sum 6 输出描述 序列长度 3 补充说明 输入
  • HJ29 字符串加解密

    描述 1 对输入的字符串进行加解密 并输出 2 加密方法为 当内容是英文字母时则用该英文字母的后一个字母替换 同时字母变换大小写 如字母a时则替换为B 字母Z时则替换为a 当内容是数字时则把该数字加1 如0替换1 1替换2 9替换0 其他字
  • 使用ffmpeg实现给音频,视频添加水印的操作

    本文主要针对ffmpeg进行整理 从而解决在现实中可能存在的问题 1 下载配置ffmpeg 这里参考的是 Java后台用ffmpeg命令给视频添加水印 身后有尾巴 博客园 cnblogs com 1 先去ffmpeg官网下载其压缩包 Dow
  • ElasticSearch详细笔记( 从入门到入土)

    文章目录 1 ElasticSearch概述 1 1 Elasticsearch 是什么 1 2 全文搜索引擎 1 3 Elasticsearch And Solr 2 ElasticSearch安装 2 1 下载和安装 2 2 可能存在的

随机推荐

  • 蓝桥杯——青蛙过河(JAVA)

    题目 小青蛙住在一条河边 它想到河对岸的学校去学习 小青蛙打算经过河里 的石头跳到对岸 河里的石头排成了一条直线 小青蛙每次跳跃必须落在一块石头或者岸上 不过 每块石头有一个高度 每次小青蛙从一块石头起跳 这块石头的高度就 会下降 1 当石
  • 关闭IDEA证书信任提示

    原因是你破解了idea idea检测弹出警告 关闭提示的方法
  • Acwing-4510. 寻宝!大冒险!

    本题的突破口在于已知B的左下角为1 A中1的数量是有限的 所以我们可以枚举一下 把B的左下角依次放到A的每一个1的位置上 A最多有1000个1 最多会放1000次 枚举完之后我们再判断一下 如果放到某个A的位置上之后 能否匹配 如何判断A的
  • DNS检测命令dig工具的安装方法

    关于dig命令的使用方法 可见 dns解析设置检测工具dig使用方法及用例里有说明 但是这个命令 不是系统自动的 或是说 一般的系统安装 默认是没有安装到该软件包的 也就是没有该命令了 不过要安装也简单 要用时使用yum安装下即可 dig命
  • get请求中params参数的使用

    一 当发送一个get请求的时候 如果有参数 那么参数应该怎么处理呢 比如 百度阅读里面 查询书的列表 点击进去 它是一个get请求 地址是 https yuedu baidu com book list 0 fr indextop 怎么可以
  • 常用访问路径

    http www json cn
  • (六)springcloud之Nacos集群与持久化配置-3

    模块 1 父工程 管理版本 2 公共模块 Common API 3 测试模块 NacosClusterConfigConsumer80 版本 springboot 2 7 6 springcloud 2021 0 5 spring clou
  • 跑跑yolov5吧

    跑跑yolov5 如果只是要快速上手训练得到训练结果 我的建议是直接使用b导的代码 别问为什么 问就是我是b导小迷弟 但是 b导的代码好像是自己写的 会存在 下载的权重文件是pth 最后得到的文件是pth文件 的问题 因为我想要用来跑自动标
  • c语言数字中英文转换器,C现代实现方法 数字转换为英文单词

    include int main int d 0 a 0 b 0 printf Enter a two digit number scanf d d if d 10 0 d 100 0 perror d exit 1 a d 10 b d
  • react v6路由拦截

    本文展示路由的配置以及路由拦截的实现方法 版本 react 17 0 2 react router dom 6 0 2 代码 App js import React from react import RootRoutes from rou
  • P1085不高兴的津津-C++编程解析-分支

    解题思路 通过题目可知 输入是津津一周内每天校内学习和校外学习的时间 由输入我们可以计算出津津每天的学习总时间 如果当天的学习时间比前一天的学习时间长 津津的不高兴程度就大 通过对7天的遍历 我们可以得出津津不高兴程度最大的那一天 题目中告
  • distinct与group by的查询效率对比

    数据量 1069753条 数据库 distinct 第二次 group by 第二次 版本 Drios 1 788s 0 751s 2 554s 0 637s 5 1 0 数据量 1021343条 Spark 6s 2s 3s 2s 3 0
  • laravel Dcat Admin Class name must be a valid object or a string

    1 问题 安装Laravel后台系统Dcat Admin 2 x报错 Class name must be a valid object or a string 2 解决 step1 更新compoer composer update st
  • LINUX安装erlang

    下载erlang erlang下载 http www erlang org download otp src 18 0 tar gz 安装 1 安装erlang tar xvzf otp src 18 0 tar gz cd otp src
  • 计算机视觉论文-2021-03-31

    本专栏是计算机视觉方向论文收集积累 时间 2021年3月31日 来源 paper digest 欢迎关注原创公众号 计算机视觉联盟 回复 西瓜书手推笔记 可获取我的机器学习纯手推笔记 直达笔记地址 机器学习手推笔记 GitHub地址 1 T
  • Springboot整合RabbitMQ

    一 Springboot整合RabbitMQ的代码实现 1 引入Springboot整合RabbitMQ的依赖
  • html怎么把图片左移_css怎么让图片向左移动?

    css怎么让图片向左移动 下面本篇文章给大家介绍一下使用CSS让图片向左移动的方法 有一定的参考价值 有需要的朋友可以参考一下 希望对大家有所帮助 css3 功能非常强大 几行代码就可以做到非常不错的移动效果 算不上动态页面 css让图片向
  • python主要内容总结_Python基础知识点总结【面试必过】

    数据类型 数字类型 int float bool True or False complex 字符串 str or 相关操作 通过下标获取内容 对字符串进行多步操作前一般需要进行切片 保证原字符串不会随着操作变化 注意 起始坐标和结束坐标都
  • react获取表单form对象的两种方式,获取form后进行重置

    1 函数组件 const CreateForm React FC
  • 沁恒CH32V307使用记录:GPIO与EXTI

    文章目录 目的 GPIO 通用输入输出接口 基础说明 初始化 输出 输入与电平读取 锁定机制 EXTI 外部中断 基础说明 使用演示 总结 目的 GPIO是单片机最基础的功能 EXTI最常用的场景就是GPIO用于输入时使用 这篇文章将对CH