当前位置:网站首页>2、GPIO相关操作
2、GPIO相关操作
2022-07-06 03:20:00 【rou252051452】
1、概述
STM32的GPIO操作归类到RTT的pin设备中,本文参考RTT关于PIN设备的相关说明(RT-Thread 文档中心)进行编写及测试。共涉及到如下几个方面
- GPIO引脚的获取
- GPIO输出控制
- GPIO输入获取
- GPIO的外部中断实现
2、引脚编号的获取
RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。官方提供了3种方式可以获取引脚编号:
- 使用API
- 使用宏定义
- 查看驱动文件
2.1驱动文件更新
API获取方式通过函数rt_pin_get实现,程序如下
rt_base_t rt_pin_get(const char *name)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
RT_ASSERT(name[0] == 'P');
if(_hw_pin.ops->pin_get == RT_NULL)
{
return -RT_ENOSYS;
}
return _hw_pin.ops->pin_get(name);
}
查看初始化阶段对_hw_pin->ops的设置情况如下
const static struct rt_pin_ops _stm32_pin_ops =
{
stm32_pin_mode,
stm32_pin_write,
stm32_pin_read,
stm32_pin_attach_irq,
stm32_pin_dettach_irq,
stm32_pin_irq_enable,
};
struct rt_pin_ops
{
void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);
void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);
int (*pin_read)(struct rt_device *device, rt_base_t pin);
rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args);
rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);
rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);
rt_base_t (*pin_get)(const char *name);
};
如上ops中并没有pin_get函数的实现方式,所以我们需要更新drv_gpio驱动文件。使用C:\RT-ThreadStudio\repo\Extract\Board_Support_Packages\RealThread\STM32H750-RT-ART-Pi\1.2.1\libraries\drivers的drv_gpio.c替换目录下得驱动文件,同步更新.h文件。查看更新后内容如下
const static struct rt_pin_ops _stm32_pin_ops =
{
stm32_pin_mode,
stm32_pin_write,
stm32_pin_read,
stm32_pin_attach_irq,
stm32_pin_dettach_irq,
stm32_pin_irq_enable,
stm32_pin_get,
};
2.2实现方式
2.2.1使用API
static rt_uint32_t reset_pin = 0;
reset_pin = rt_pin_get("PF.9");
rt_base_t rt_pin_get(const char *name)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
RT_ASSERT(name[0] == 'P');
if(_hw_pin.ops->pin_get == RT_NULL)
{
return -RT_ENOSYS;
}
return _hw_pin.ops->pin_get(name);
}
如上所示,rt_pin_get首先对输入的字符串进行了严重是否为字母P开头,最终调用的函数为_hw_pin.ops->pin_get(name);内容如下:
static rt_base_t stm32_pin_get(const char *name)
{
rt_base_t pin = 0;
int hw_port_num, hw_pin_num = 0;
int i, name_len;
name_len = rt_strlen(name);
if ((name_len < 4) || (name_len >= 6)) //进行字符串长度验证PA.0 PA.15 最短4,最长5
{
return -RT_EINVAL;
}
if ((name[0] != 'P') || (name[2] != '.')) //字符串第一个必须为P 第三个必须为.
{
return -RT_EINVAL;
}
if ((name[1] >= 'A') && (name[1] <= 'Z')) //端口范围在A-Z
{
hw_port_num = (int)(name[1] - 'A'); //端口编号计算,A-Z转换为0-25
}
else
{
return -RT_EINVAL;
}
for (i = 3; i < name_len; i++) //根据字符串的第4个进行编号转换PA.0=0 PA.15 = 15
{
hw_pin_num *= 10;
hw_pin_num += name[i] - '0';
}
pin = PIN_NUM(hw_port_num, hw_pin_num); //进行最后引脚编号的处理
return pin;
}
最终调用宏定义如下来进行引脚编号的获取
#define PIN_NUM(port, no) (((((port) & 0xFu) << 4) | ((no) & 0xFu)))
测算结果如下表,后续IO以此类推
name | port | no | result | name | port | no | result |
PA.0 | 0 | 0 | 0 | PC.0 | 2 | 0 | 32 |
PA.1 | 0 | 1 | 1 | PC.1 | 2 | 1 | 33 |
PA.2 | 0 | 2 | 2 | PC.2 | 2 | 2 | 34 |
PA.3 | 0 | 3 | 3 | PC.3 | 2 | 3 | 35 |
PA.4 | 0 | 4 | 4 | PC.4 | 2 | 4 | 36 |
PA.5 | 0 | 5 | 5 | PC.5 | 2 | 5 | 37 |
PA.6 | 0 | 6 | 6 | PC.6 | 2 | 6 | 38 |
PA.7 | 0 | 7 | 7 | PC.7 | 2 | 7 | 39 |
PA.8 | 0 | 8 | 8 | PC.8 | 2 | 8 | 40 |
PA.9 | 0 | 9 | 9 | PC.9 | 2 | 9 | 41 |
PA.10 | 0 | 10 | 10 | PC.10 | 2 | 10 | 42 |
PA.11 | 0 | 11 | 11 | PC.11 | 2 | 11 | 43 |
PA.12 | 0 | 12 | 12 | PC.12 | 2 | 12 | 44 |
PA.13 | 0 | 13 | 13 | PC.13 | 2 | 13 | 45 |
PA.14 | 0 | 14 | 14 | PC.14 | 2 | 14 | 46 |
PA.15 | 0 | 15 | 15 | PC.15 | 2 | 15 | 47 |
PB.0 | 1 | 0 | 16 | PD.0 | 3 | 0 | 48 |
PB.1 | 1 | 1 | 17 | PD.1 | 3 | 1 | 49 |
PB.2 | 1 | 2 | 18 | PD.2 | 3 | 2 | 50 |
PB.3 | 1 | 3 | 19 | PD.3 | 3 | 3 | 51 |
PB.4 | 1 | 4 | 20 | PD.4 | 3 | 4 | 52 |
PB.5 | 1 | 5 | 21 | PD.5 | 3 | 5 | 53 |
PB.6 | 1 | 6 | 22 | PD.6 | 3 | 6 | 54 |
PB.7 | 1 | 7 | 23 | PD.7 | 3 | 7 | 55 |
PB.8 | 1 | 8 | 24 | PD.8 | 3 | 8 | 56 |
PB.9 | 1 | 9 | 25 | PD.9 | 3 | 9 | 57 |
PB.10 | 1 | 10 | 26 | PD.10 | 3 | 10 | 58 |
PB.11 | 1 | 11 | 27 | PD.11 | 3 | 11 | 59 |
PB.12 | 1 | 12 | 28 | PD.12 | 3 | 12 | 60 |
PB.13 | 1 | 13 | 29 | PD.13 | 3 | 13 | 61 |
PB.14 | 1 | 14 | 30 | PD.14 | 3 | 14 | 62 |
PB.15 | 1 | 15 | 31 | PD.15 | 3 | 15 | 63 |
2.2.2使用宏定义
#define LED0_PIN GET_PIN(F, 9)
#define __STM32_PORT(port) GPIO##port##_BASE
#if defined(SOC_SERIES_STM32MP1)
#define GET_PIN(PORTx,PIN) (GPIO##PORTx == GPIOZ) ? (176 + PIN) : ((rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x1000UL) )) + PIN))
#else
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
#endif
宏定义 | PORTx | 16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) ) | PIN | result | |
GET_PIN(A, 0) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 1) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 2) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 3) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(B, 0) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 0 | 16 |
GET_PIN(B, 1) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 1 | 17 |
GET_PIN(B, 2) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 2 | 18 |
结果与API实现方式结果相同。
2.2.3查看驱动文件
该方式在更新驱动文件drv_gpio后没有提供官方文档说明的查看驱动方式,这里不进行详细说明。
3、GPIO输出控制
我们通过控制主控板上的LED灯来验证GPIO的输出功能。
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#define LED0 GET_PIN(B, 0)
void example_gpio()
{
rt_pin_mode(LED0,PIN_MODE_OUTPUT); //设置管脚为输出
rt_pin_write(LED0,PIN_LOW);
}
MSH_CMD_EXPORT(example_gpio, example_gpio)
4、GPIO输入控制
通过板载按键来控制核心板的LED灯亮灭,按下后灯亮,松开后灯灭
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#define LED0 GET_PIN(B, 0)
void example_gpio()
{
static rt_uint32_t key = 0;
rt_pin_mode(LED0,PIN_MODE_OUTPUT); //设置管脚为输出
rt_pin_write(LED0,PIN_LOW);
key = rt_pin_get("PA.0"); //获取引脚编号
rt_pin_mode(key,PIN_MODE_INPUT); //设置管脚为输入
while(1)
{
if(rt_pin_read(key) == PIN_HIGH) //读取输入状态
{
rt_pin_write(LED0,PIN_LOW);
}
else
{
rt_pin_write(LED0,PIN_HIGH);
}
rt_thread_mdelay(1000);
}
}
MSH_CMD_EXPORT(example_gpio, example_gpio)
5、GPIO中断
通过板载按键来控制核心板的LED灯亮灭,按下后为上升沿中断,灯亮。松开后为下降沿中断,灯灭
6、驱动过程详解
6.1模式设置
模式设置调用的函数为rt_pin_mode,最终调用的函数为ops->pin_mode,为drv_gpio.c中的stm32_pin_mode函数,具体内容如下
static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
GPIO_InitTypeDef GPIO_InitStruct;
//识别端口号是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return;
}
/*
进行GPIO初始化结构体的相关赋值
默认配置为推挽输出,高速,无上下拉
*/
GPIO_InitStruct.Pin = PIN_STPIN(pin);
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
/*
根据输入的模式参数进行相关内容修改。
*/
if (mode == PIN_MODE_OUTPUT)
{
/* output setting */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT)
{
/* input setting: not pull. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT_PULLUP)
{
/* input setting: pull up. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
}
else if (mode == PIN_MODE_INPUT_PULLDOWN)
{
/* input setting: pull down. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
}
else if (mode == PIN_MODE_OUTPUT_OD)
{
/* output setting: od. */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
/*
进行GPIO初始化,根据宏定义PIN_STPORT识别出GPIOx的地址
*/
HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
}
6.2输出电平控制
输出电平调用的函数为rt_pin_write,最终调用的函数为ops->pin_write,为drv_gpio.c中的stm32_pin_write函数,具体内容如下
static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
GPIO_TypeDef *gpio_port;
uint16_t gpio_pin;
//根据宏定义将引脚序号转换为port和pin信息,调用HAL库函数进行输出
if (PIN_PORT(pin) < PIN_STPORT_MAX)
{
gpio_port = PIN_STPORT(pin);
gpio_pin = PIN_STPIN(pin);
HAL_GPIO_WritePin(gpio_port, gpio_pin, (GPIO_PinState)value);
}
}
6.3输入电平获取
输入电平获取调用的函数为rt_pin_read,最终调用的函数为ops->pin_read,为drv_gpio.c中的stm32_pin_read函数,具体内容如下
static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{
GPIO_TypeDef *gpio_port;
uint16_t gpio_pin;
int value = PIN_LOW;
//根据宏定义将引脚序号转换为port和pin信息,调用HAL库函数进行输入信息的获取
if (PIN_PORT(pin) < PIN_STPORT_MAX)
{
gpio_port = PIN_STPORT(pin);
gpio_pin = PIN_STPIN(pin);
value = HAL_GPIO_ReadPin(gpio_port, gpio_pin);
}
return value;
}
6.4绑定引脚中断回调函数
绑定中断函数调用的是rt_pin_attach_irq,最终调用的函数为ops->pin_attach_irq,为drv_gpio.c中的stm32_pin_attach_irq函数,具体内容如下
只进行了rtt下pin设备的中断管脚模式和中断回调函数的赋值,未进行实质性的STM32的底层HAL库配置。
static rt_err_t stm32_pin_attach_irq(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args)
{
rt_base_t level;
rt_int32_t irqindex = -1;
//识别端口是否正确
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
//识别中断源为几 0-15
irqindex = bit2bitno(PIN_STPIN(pin));
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqindex].pin == pin &&
pin_irq_hdr_tab[irqindex].hdr == hdr &&
pin_irq_hdr_tab[irqindex].mode == mode &&
pin_irq_hdr_tab[irqindex].args == args)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
if (pin_irq_hdr_tab[irqindex].pin != -1)
{
rt_hw_interrupt_enable(level);
return RT_EBUSY;
}
pin_irq_hdr_tab[irqindex].pin = pin;
pin_irq_hdr_tab[irqindex].hdr = hdr;
pin_irq_hdr_tab[irqindex].mode = mode;
pin_irq_hdr_tab[irqindex].args = args;
rt_hw_interrupt_enable(level);
return RT_EOK;
}
具体的HAL库底层驱动配置在rt_pin_irq_enable函数中实现,该函数最终调用函数为stm32_pin_irq_enable内容如下:
static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
rt_uint32_t enabled)
{
const struct pin_irq_map *irqmap;
rt_base_t level;
rt_int32_t irqindex = -1;
GPIO_InitTypeDef GPIO_InitStruct;
//识别IO是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
//使能中断,进行是能的配置。
if (enabled == PIN_IRQ_ENABLE)
{
//获取管脚序号
irqindex = bit2bitno(PIN_STPIN(pin));
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
//pin_irq_hdr_tab的内容在stm32_pin_attach_irq时进行了赋值。
if (pin_irq_hdr_tab[irqindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_ENOSYS;
}
//根据管脚获取中断序号 EXTI0_IRQn-EXTI4_IRQn或EXTI9_5_IRQn
irqmap = &pin_irq_map[irqindex];
//配置GPIO初始化结构体
/* Configure GPIO_InitStructure */
GPIO_InitStruct.Pin = PIN_STPIN(pin);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
switch (pin_irq_hdr_tab[irqindex].mode)
{
case PIN_IRQ_MODE_RISING:
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
break;
case PIN_IRQ_MODE_FALLING:
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
break;
case PIN_IRQ_MODE_RISING_FALLING:
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
break;
}
HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
//设置中断优先级,使能中断
HAL_NVIC_SetPriority(irqmap->irqno, 5, 0);
HAL_NVIC_EnableIRQ(irqmap->irqno);
pin_irq_enable_mask |= irqmap->pinbit;
rt_hw_interrupt_enable(level);
}
//禁止中断
else if (enabled == PIN_IRQ_DISABLE)
{
irqmap = get_pin_irq_map(PIN_STPIN(pin));
if (irqmap == RT_NULL)
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
//复位GPIO
HAL_GPIO_DeInit(PIN_STPORT(pin), PIN_STPIN(pin));
pin_irq_enable_mask &= ~irqmap->pinbit;
if ((irqmap->pinbit >= GPIO_PIN_5) && (irqmap->pinbit <= GPIO_PIN_9))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if ((irqmap->pinbit >= GPIO_PIN_10) && (irqmap->pinbit <= GPIO_PIN_15))
{
if (!(pin_irq_enable_mask & (GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
rt_hw_interrupt_enable(level);
}
else
{
return -RT_ENOSYS;
}
return RT_EOK;
}
边栏推荐
- Self made CA certificate and SSL certificate using OpenSSL
- Linear regression and logistic regression
- An article about liquid template engine
- February 14, 2022 Daily: Google long article summarizes the experience of building four generations of TPU
- Custom attribute access__ getattribute__/ Settings__ setattr__/ Delete__ delattr__ method
- Leetcode problem solving -- 108 Convert an ordered array into a binary search tree
- js凡客banner轮播图js特效
- Pytorch基础——(2)张量(tensor)的数学运算
- 这些不太会
- NR modulation 1
猜你喜欢
OCR文字識別方法綜述
Performance test method of bank core business system
My C language learning record (blue bridge) -- under the pointer
XSS challenges bypass the protection strategy for XSS injection
MPLS experiment
I sorted out a classic interview question for my job hopping friends
遥感图像超分辨率论文推荐
Web security SQL injection vulnerability (1)
Derivation of anti Park transform and anti Clarke transform formulas for motor control
蓝色样式商城网站页脚代码
随机推荐
Restful style
指针笔试题~走近大厂
How to choose PLC and MCU?
Audio audiorecord binder communication mechanism
深入刨析的指针(题解)
ASU & OSU | model based regularized off-line meta reinforcement learning
Redis SDS principle
Buuctf question brushing notes - [geek challenge 2019] easysql 1
记录一下逆向任务管理器的过程
js凡客banner轮播图js特效
Huawei, H3C, Cisco command comparison, mind map form from the basic, switching, routing three directions [transferred from wechat official account network technology alliance station]
3857墨卡托坐标系转换为4326 (WGS84)经纬度坐标
Shell 传递参数
EDCircles: A real-time circle detector with a false detection control 翻译
Reverse repackaging of wechat applet
Selenium share
SAP ALV单元格级别设置颜色
OCR文字识别方法综述
These are not very good
Inherit day01