当前位置:网站首页>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;
}
边栏推荐
- Problems encountered in 2022 work IV
- 施努卡:什么是视觉定位系统 视觉系统如何定位
- Buuctf question brushing notes - [geek challenge 2019] easysql 1
- IPv6 jobs
- Remote Sensing Image Super-resolution and Object Detection: Benchmark and State of the Art
- How to choose PLC and MCU?
- I sorted out a classic interview question for my job hopping friends
- Inherit day01
- JS音乐在线播放插件vsPlayAudio.js
- XSS challenges bypass the protection strategy for XSS injection
猜你喜欢
随机推荐
Mysqldump data backup
Analyze menu analysis
Handwriting database client
EDCircles: A real-time circle detector with a false detection control 翻译
【指针训练——八道题】
My C language learning record (blue bridge) -- under the pointer
Mysql database operation
Leetcode problem solving -- 108 Convert an ordered array into a binary search tree
Pytorch基础——(2)张量(tensor)的数学运算
遥感图像超分辨重建综述
jsscript
电机控制反Park变换和反Clarke变换公式推导
Tidb ecological tools (backup, migration, import / export) collation
How to write compile scripts compatible with arm and x86 (Makefile, cmakelists.txt, shell script)
Buuctf question brushing notes - [geek challenge 2019] easysql 1
出现Permission denied的解决办法(750权限谨慎使用)
Redis SDS principle
. Net 6 and Net core learning notes: Important issues of net core
Problems encountered in 2022 work IV
Research on cooperative control of industrial robots