一、GPIO驱动的框架
在pin.c 中,会有一个GPIO 的注册函数:rt_device_pin_register
通过里面的这三个函数来操作引脚,这里使用了全局变量,说明永远只能有一个驱动程序。
1.1 读函数
函数的代码如下,其中传入的参数 buffer 是 rt_device_pin_status 这个结构体类型的参数。
rt_device_pin_status 这个结构体里面有两个值,我们之前的buffer 是可以往里面存入数据,现在是通过这个引脚pin把硬件数据传递给驱动程序,通过status 把驱动程序返回的数据传给应用程序。
1.2 写函数
写函数和读函数基本一样,都是用rt_device_pin_status 这个结构体来控制使用哪个引脚,把这个引脚写成什么状态。
1.3 硬件相关的函数
上面的读写函数可以算是框架层,在里面的函数中可以发现里面会调用到底层的函数,涉及到一个“ops” 结构体,里面有各种函数,和上面的应用程序相对应。
二、分析GPIO的驱动代码
2.1 初始化驱动
在 drv_gpio.c 中,rt_hw_pin_init 函数使能各个串口的时钟,最后注册了一个驱动的程序,驱动程序的名字叫“pin”,提供了一些引脚操作的相关函数
return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
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,
};
注册函数中的 “name” 其实就是“pin”,以后使用“pin”的时候,首先要找到这个名字为“pin”的驱动程序,然后打开,读写它。
引出一个问题,读写引脚的时候都是传入整数,这里的整数是如何和具体的引脚挂钩呢?里面其实是有一个数组的索引,数组的每一项表示一个引脚。
2.2 APP编写大概思路
里面最主要是要分辨清楚 0/1号引脚对应的是哪个真正的引脚,以及引脚编号是什么。
dev = rt_device_find("pin"); //找到刚刚定义的驱动
rt_device_open(dev); //打开这个驱动程序
/* 设置0 号引脚读 */
struct rt_device_pin_mode mode;
mode.pin = 0;
// PIN_MODE_OUTPUT,PIN_MODE_INPUT_PULLUP,PIN_MODE_INPUT_PULLDOWN,PIN_MODE_OUTPUT_OD
mode.mode = PIN_MODE_INPUT; //上面的各种模式
rt_device_control(dev, 0, &mode);
/* 现在要去读 这个设备中的某个引脚 eg.0 */
rt_device_pin_status status; //需要先构造一个status 结构体
status.pin = 0; //在这个结构体中指定读哪个引脚
rt_device_read(dev, &status, sizeof(status));
rt_kprintf("gpio 0 = %d\n", status.status);
/* 设置1 号引脚输出 */
struct rt_device_pin_mode mode;
mode.pin = 1;
// PIN_MODE_INPUT,PIN_MODE_INPUT_PULLUP,PIN_MODE_INPUT_PULLDOWN,PIN_MODE_OUTPUT_OD
mode.mode = PIN_MODE_OUTPUT;
rt_device_control(dev, 0, &mode);
rt_device_pin_status status;
status.pin = 1;
status.status = 0;
rt_device_write(dev, &status, sizeof(status));
三、编写一个驱动LED的程序
思路是:查看原理图,确定使用哪个引脚;查看RT-Thread的pin驱动程序,确定引脚编号;编写APP
这里我选择PA11, 对应 __STM32_PIN(11, A, 10)
(下面的代码是基于韦东山老师的课程代码修改)
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#include <stdlib.h>
#include <drivers\pin.h>
#define DBG_TAG "led"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#ifdef FINSH_USING_MSH
/* led on
* led off
*/
static int led(int argc, char **argv)
{
rt_device_t pin_dev;
struct rt_device_pin_mode mode;
struct rt_device_pin_status status;
int len;
if (argc != 2)
{
rt_kprintf("Usage: \n");
rt_kprintf("led <on | off> - set led an on/off\n");
return -RT_ERROR;
}
else
{
/* 找到设备 */
pin_dev = rt_device_find("pin");
if (!pin_dev)
{
rt_kprintf("can not get pin drv\n");
return -RT_ERROR;
}
/* 设置引脚输出模式,结构体在上面定义 */
rt_device_open(pin_dev, RT_DEVICE_OFLAG_RDWR);
mode.pin = 11;
mode.mode = PIN_MODE_OUTPUT;
rt_device_control(pin_dev, 0, &mode);
/* 设置引脚输出的状态,结构体在上面定义 */
status.pin = 11;
if (!strcmp(argv[1], "on"))
status.status = 0;
else
status.status = 1;
len = rt_device_write(pin_dev, 0, &status, sizeof(status));
rt_kprintf("rt_device_write ret = %d, err = %d\n", len, rt_get_errno());
/* 关闭驱动程序 */
rt_device_close(pin_dev);
/* 调用驱动的read函数 */
}
return RT_EOK;
}
MSH_CMD_EXPORT(led, led function);
#endif /* FINSH_USING_MSH */
总结来说步骤如下:
1. 找到注册的设备 :rt_device_find;并打开这个设备rt_device_open
2. 设置模式:mode (输出还是输入)
3. 设置引脚的输出状态(高低电平)
4. 关闭驱动程序