该工具类为北邮-国院-电信工程及管理专业-通信方向-大三课程-微处理器设计(Microprocessor System Design)- Lab 2中英方给的Driver类 gpio.c。
由于上课课件中并未对该Driver进行解读,因此在写Lab2的时候就会造成很多的困扰。因此在写完Lab 2之后写了这篇博客希望对还没有做Lab 2的同学及学弟学妹带来帮助。
在本博客中的解读皆基于对该工具类中的方法基于原英文注释的翻译及博主使用体验编写。博客末尾已附上该gpio.c的源码。若有解读错误,欢迎指正。
直接看例子会更快哦!
如果设置了callback但在点击对应PIN还没有反应的,下拉到 设置回调函数 | set_callback 看解决方法。
设置PIN为Input或Output | gpio_set_mode
按照课程PPT的内容,我们在设置PIN
- 为输入时仅考虑设置其为Pull Up模式或者 Pull Down模式。不考虑其他情况
该部分为直接引用博主Yngz_Miao的博客【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图)。
具体功能果园学子仍以PPT中为准。
Pull Up上拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。
- I/O端口悬空(在无信号输入)的情况下,输入端的电平可以保持在高电平
- 在I/O端口输入为低电平的时候,输入端的电平也还是低电平。
下拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。
- 在I/O端口悬空(在无信号输入)的情况下,输入端的电平为低电平;
- 在I/O端口输入为高电平的时候,输入端的电平为高电平。
调用方法:
void gpio_set_mode(Pin pin, PinMode mode){}
传入参数解释:
-
Pin pin:被设置的PIN
-
PinMode:所要设置的模式。该项可传入选择有
// The modes configure the GPIO pin as follows: // - Reset: resets to a default state. // - Input: sets as a high impedance input. // - Output: sets as a push-pull output. // - PullUp: enables the internal pull-up resistor // with the pin configured as an input, and // sets the output to logic high (through // the pull-up resistor). // - PullDown: enables the internal pull-down resistor // with the pin configured as an input, and // sets the output to logic low (through the // pull-down).
例子:
-
设置PB_0为上拉(Pull Up)输入模式
gpio_set_mode(PB_0, PullUp);
-
设置PA_0为下拉(Pull Down)输入模式
gpio_set_mode(PA_0, PullDown);
调用方法与设置input一致。直接看例子
例子: 设置PA_1为输出端口
gpio_set_mode(PA_1, Output);读取PIN 读取单个PIN | gpio_get
调用方法:
int gpio_get(Pin pin) {}
传入参数解释:
- Pin pin:要读取PIN
返回值:为整数类型,而不是二进制数。
例子: 读取PB_0,假设此时PB_0为高电平。
int status = gpio_get_range(PB_0); //status = 1;
- 若高电平时,返回1
- 若低电平时,返回0
调用方法:
unsigned int gpio_get_range(Pin pin_base, int count) {{}
传入参数解释:
- Pin pin_base:从哪一位PIN开始读
- int count:一共要读取多少位。读一位则设为1
返回值:为整数类型,而不是二进制数。
例子:读取PB_1、PB_2、 PB_3,假设此时PB_1、PB_2、 PB_3分为为1、1、0
LINE = gpio_get_range(PB_1, 3); //LINE = 6读取时的坑点
两个读取函数使用时,并不是说你将所要读取的PIN设了,它就能马上读到。而是还要再按另一个其他的PIN才能将原来的读进去。
例如:现在我用a = gpio_get_range(PB_1, 3)读取3个PINs。进入Debug模式,假设运行后三个PINs为000,那么在我设置这三个PINs为001时(将PB_1设为HIGH),a仍然为(十进制的)0。在我(随意按一个没有set_mode的键)按PB_10后,a才变为(十进制的)1。
设置PIN的值每一个PIN只有高电平和低电平两个值。
- HIGH:1
- LOW:0
调用方法:
void gpio_set(Pin pin, int value) {}
传入参数解释:
- Pin pin:要设置PIN
- int value:对应要设置的值。注意这个值的类型为int类型的,这里只能输入0或1,0对应为LOW。(其他的值我也没测试)
例子:设置PA_1为高电平
gpio_set(PA_1, 1);设置一个范围内的PIN | gpio_set_range
调用方法:
void gpio_set_range(Pin pin_base, int count, int value) {}
传入参数解释:
- Pin pin:要从哪一个PIN开始设置
- int count:要设置多少个PIN
- int value:对应要设置的值。注意这个值的类型为int类型的。也就是说,如果我们要设置3 PINs,当传入参数value的值为4时,这三个PINs会设置为100(左边为MSB)
- 不允许设置的值超过所设置PIN的数量能表示的值,例如我要设置3个PINs,参数value传入十进制的8,这是会报错的。
例子: 将十进制的7用三个PB_1、PB_2、 PB_3表示
gpio_set_range(PB_1, 3, 7);
例子: 利用PA_0 ~ PA_7这8个PIN来表示0~8的one-hot code。即,当输入十进制(int)的7时,PA_7为HIGH,其他都为LOW
#include设置PIN为原来的相反值 | gpio_togglegpio_set_range(PA_0, 8, pow(2, 7));//pow(2,x) x为要进行独热编码的十进制的值
调用方法:
void gpio_toggle(Pin pin) {}
传入参数解释:
- Pin pin:要设置的PIN
例子: 将PB_0的值倒置。假设原PB_0 = LOW
gpio_toggle(PB_0); //执行完后 PB_0 变为 HIGH设置上升沿(儿)/下降沿(儿)触发 | gpio_set_trigger
调用方法:
void gpio_set_trigger(Pin pin, TriggerMode trig) {}
传入参数解释:
- Pin pin:所要设置的Pin
- TriggerMode trig:对应触发的模式。可选Rising、Falling。
例子: 设置PB_0为上升沿(儿)触发
gpio_set_trigger(PB_0, Rising);设置回调函数 | gpio_set_callback
设置某个PIN触发后会调用什么函数来响应。(按下按钮触发函数就是设置这个!)
调用方法:
void gpio_set_callback(Pin pin, void (*callback)(int status)){}
传入参数解释:
- Pin pin:所要设置的Pin
- void (*callback)(int status):所要触发函数的指针(其实就是函数名)。注意这个函数的传入参数一定要是int status,参数一定有且只有这个,函数内可不使用这个函数。
例子: 设定PB_0为Push Button,按下后调用void PB_ISR(int status){}函数
gpio_set_callback(PB_0, PB_ISR);
设置上了面代码,在Debug模式中点击PB_0仍没有触发PB_ISR解决方法: 确保将external interrupts 的 source的值是B。具体步骤为:
-
进入Debug模式
-
顶部工具栏 Peripherals -> External Interrupts。(点开Peripherals 没有内容就是没有进入debug模式!)
-
Source设置为B
例子:设定PB_0为上升沿儿触发的Push Button,按下后调用void PB_ISR(int status){}函数
gpio_set_trigger(PB_0, Rising); gpio_set_callback(PB_0, PB_ISR);工具类源码 gpio.c
#include "platform.h"
#include "gpio.h"
uint32_t IRQ_status;
uint32_t IRQ_port_num;
uint32_t IRQ_pin_index;
uint32_t EXTI_port_set;
uint32_t prioritygroup;
uint32_t priority;
static void (*GPIO_callback)(int status);
void gpio_toggle(Pin pin) {
// Toggles a GPIO pin.
// In the absence of a pin toggle register, can be easily
// implemented with:
// gpio_set(pin, !gpio_get(pin));
gpio_set(pin, !gpio_get(pin));
}
void gpio_set(Pin pin, int value) {
// Sets the selected pin to the specified value.
GPIO_TypeDef* p = GET_PORT(pin);
uint32_t pin_index = GET_PIN_INDEX(pin);
MODIFY_REG(p->ODR,1UL<IDR,(1<>pin_index;
}
void gpio_set_range(Pin pin_base, int count, int value) {
// Sets a range of pins to the specified value.
// This can be used to write to an entire port, or just
// a subset of the (consecutive) pins on a port.
// The output pin_base should be set to the LSB of
// value. Pin (pin_base + 1) should be LSB + 1, etc.
// The mask for the value parameter should be:
// ((1 << count) - 1).
GPIO_TypeDef* p = GET_PORT(pin_base);
uint32_t pin_index = GET_PIN_INDEX(pin_base);
MODIFY_REG(p->ODR,((1UL<IDR,(((1 << count) - 1)<>pin_index;
}
void gpio_set_mode(Pin pin, PinMode mode) {
// Sets the output mode of a pin.
// If the clock for the pin's port needs to be enabled, it
// should be done here.
// The modes configure the GPIO pin as follows:
// - Reset: resets to a default state.
// - Input: sets as a high impedance input.
// - Output: sets as a push-pull output.
// - PullUp: enables the internal pull-up resistor
// with the pin configured as an input, and
// sets the output to logic high (through
// the pull-up resistor).
// - PullDown: enables the internal pull-down resistor
// with the pin configured as an input, and
// sets the output to logic low (through the
// pull-down).
GPIO_TypeDef* p = GET_PORT(pin);
uint32_t pin_index = GET_PIN_INDEX(pin);
switch(mode) {
case Reset: // input floating mode by default
if (pin_index < 8)
MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 4UL<<((pin_index)*4)); //0100
else
MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 4UL<<((pin_index-8)*4));
break;
case Input: // same as reset
if (pin_index < 8)
MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 4UL<<((pin_index)*4)); //0100
else
MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 4UL<<((pin_index-8)*4));
break;
case Output: // output push-pull max 2 MHz
if (pin_index < 8)
MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 2UL<<((pin_index)*4)); //0010
else
MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 2UL<<((pin_index-8)*4));
break;
case PullUp: // input pull-up
MODIFY_REG(p->ODR, 1UL<<((pin_index)*4), 1UL<<((pin_index)*4));
if (pin_index < 8)
MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 8UL<<((pin_index)*4)); //1000
else
MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 8UL<<((pin_index-8)*4));
break;
case PullDown: // input pull-down
MODIFY_REG(p->ODR, 1UL<<((pin_index)*4), 0UL<<((pin_index)*4));
if (pin_index < 8)
MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 8UL<<((pin_index)*4)); //1000
else
MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 8UL<<((pin_index-8)*4));
break;
}
}
void gpio_set_trigger(Pin pin, TriggerMode trig) {
// Sets the interrupt trigger for the specified pin.
// The modes are as follows:
// - None: Disable the trigger.
// - Rising: Trigger on transition from logic low to
// high.
// - Falling: Trigger on transition from logic high to
// low.
uint32_t pin_index = GET_PIN_INDEX(pin);
switch(trig){
case None:
EXTI->IMR &= ~(1<IMR |= (1<RTSR|= (1<IMR |= (1<FTSR|= (1<PR &= (0<EXTICR[0]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI0_IRQn, priority);
NVIC_EnableIRQ(EXTI0_IRQn);
break;
case 1:
//SYSCFG->EXTICR[0]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI1_IRQn, priority);
NVIC_EnableIRQ(EXTI1_IRQn);
break;
case 2:
//SYSCFG->EXTICR[0]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI2_IRQn, priority);
NVIC_EnableIRQ(EXTI2_IRQn);
break;
case 3:
//SYSCFG->EXTICR[0]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI3_IRQn, priority);
NVIC_EnableIRQ(EXTI3_IRQn);
break;
case 4:
//SYSCFG->EXTICR[1]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI4_IRQn, priority);
NVIC_EnableIRQ(EXTI4_IRQn);
break;
case 5:
case 6:
case 7:
// SYSCFG->EXTICR[1]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI9_5_IRQn, priority);
NVIC_EnableIRQ(EXTI9_5_IRQn);
break;
case 8:
case 9:
// SYSCFG->EXTICR[2]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI9_5_IRQn,3);
NVIC_EnableIRQ(EXTI9_5_IRQn);
break;
case 10:
case 11:
// SYSCFG->EXTICR[2]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI15_10_IRQn,3);
NVIC_EnableIRQ(EXTI15_10_IRQn);
break;
case 12:
case 13:
case 14:
case 15:
//SYSCFG->EXTICR[3]|= EXTI_port_set;
prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
NVIC_SetPriority(EXTI15_10_IRQn,3);
NVIC_EnableIRQ(EXTI15_10_IRQn);
break;
}
}
//Note: only four interrupt lines are implemented i.e. only use pin 0-4
void EXTI0_IRQHandler(void){
GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
IRQ_status = EXTI->PR>>IRQ_pin_index & 1;
NVIC_ClearPendingIRQ(EXTI0_IRQn);
EXTI->PR|=(1<IDR&(1<PR>>IRQ_pin_index & 1;
NVIC_ClearPendingIRQ(EXTI1_IRQn);
EXTI->PR|=(1<IDR&(1<PR>>IRQ_pin_index & 1;
NVIC_ClearPendingIRQ(EXTI2_IRQn);
EXTI->PR|=(1<IDR&(1<PR>>IRQ_pin_index & 1;
NVIC_ClearPendingIRQ(EXTI3_IRQn);
EXTI->PR|=(1<IDR&(1<PR>>IRQ_pin_index & 1;
NVIC_ClearPendingIRQ(EXTI4_IRQn);
EXTI->PR|=(1<IDR&(1<PR>>IRQ_pin_index) & 1;
NVIC_ClearPendingIRQ(EXTI9_5_IRQn);
EXTI->PR|=(1<IDR&(1<PR>>IRQ_pin_index) & 1;
NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
EXTI->PR|=(1<IDR&(1<
参考文章
Yngz_Miao,【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图),https://blog.csdn.net/qq_38410730/article/details/79858906



