GPIO,全称General-purpose input/output,通用输入输出,也就是芯片上的引脚。
GPIO的用途有很多:
- 输出控制信号
- 输入中断信号
- 模拟I2C或SPI数据线
- ADC电压采样
- 输出PWM波形
- 等等
可以参考这篇文章:硬件工程师必看:GPIO用法大汇总 - 知乎
GPIO默认为低电平
此测试代码演示如何配置 GPIO 以及如何在中断的情况下使用它。
由于本人近期不太可能用到矩阵键盘,因此矩阵键盘的例程就先放一放。
准备工作
准备两根跳线,连接GPIO18和GPIO4、GPIO19和GPIO5
在GPIO18/19产生脉冲,以在GPIO4/5上产生中断
GPIO | 描述 | 配置 |
---|---|---|
GPIO18 | 输出 | |
GPIO19 | 输出 | |
GPIO4 | 输入 | pulled up, interrupt from rising edge and falling edge |
GPIO5 | 输入 | pulled up, interrupt from rising edge |
宏定义与变量
#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
//IO口的位掩码
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0 4
#define GPIO_INPUT_IO_1 5
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0
//用于将信息从GPIO中断处理函数发送到GPIO任务函数的队列
static xQueueHandle gpio_evt_queue = NULL;
函数部分
//中断服务函数
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
uint32_t gpio_num = (uint32_t) arg;
//将GPIO编号发送到队列末尾
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
static void gpio_task_example(void* arg)
{
uint32_t io_num;
for(;;) {
//从队列接收GPIO编号
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
//打印GPIO编号,并获取对应的电平
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
}
}
}
void app_main(void)
{
//0初始化配置结构体
gpio_config_t io_conf = {};
//关中断
io_conf.intr_type = GPIO_INTR_DISABLE;
/**********配置GPIO 18/19*************/
//设置为输出模式
io_conf.mode = GPIO_MODE_OUTPUT;
//你想设置的GPIO的位掩码,e.g.GPIO18/19
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
//禁用下拉
io_conf.pull_down_en = 0;
//禁用上拉
io_conf.pull_up_en = 0;
//使配置生效
gpio_config(&io_conf);
/**********配置GPIO 4/5*************/
//上升沿触发中断
io_conf.intr_type = GPIO_INTR_POSEDGE;
//你想设置的GPIO的位掩码, use GPIO4/5 here
io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
//set as input mode
io_conf.mode = GPIO_MODE_INPUT;
//设置为高电平
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
//将GPIO4设置为上升沿和下降沿都触发中断
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);
//创建一个队列,用来处理来自中断服务函数的GPIO事件
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
//创建GPIO任务,分配2048字节给任务堆栈,优先级为10
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
//安装GPIO中断服务函数
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//为GPIO4/5添加中断服务函数句柄
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);
//删除GPIO4的中断服务函数句柄
gpio_isr_handler_remove(GPIO_INPUT_IO_0);
//再次为GPIO4添加中断服务函数句柄
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());
int cnt = 0;
while(1) {
printf("cnt: %d\n", cnt++);
vTaskDelay(1000 / portTICK_RATE_MS);
//在GPIO18/19上产生脉冲
gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
}
}