7 操作系统(RTOS)之PIN(GPIO)设备操作
7.1 基本概念介绍
RT-Thread 分为标准版本和 Nano 版本,其特点如下:
RT-Thread 标准版:拥有设备驱动框架,软件包等组件,软件包都是基于设备驱动接口来实现。
RT-Thread Nano:仅仅只是一个 RTOS 内核。没有任何组件。
Nano 是无法直接使用 RT-Thread 丰富软件包功能。
Nano 是一个面向低资源的 MCU 等芯片,不可能增加如同标准版的设备驱动框架。
Nano 需要一套统一设备驱动 API ,屏蔽不同芯片的 HAL 层的区别。方便移植工程到不同的平台。
Nano 需要一套设备驱动 API ,可以方便使用丰富软件包组件。
我们回到第二章,用操作系统IO视角来重新定义一下IO口操作。
芯片上的引脚一般分为 4 类:电源、时钟、控制与 I/O,I/O 口在使用模式上又分为 General Purpose
Input Output(通用输入 / 输出),简称 GPIO,与功能复用 I/O(如 SPI/I2C/UART 等)。
大多数 MCU 的引脚都不止一个功能。不同引脚内部结构不一样,拥有的功能也不一样。可以通过不
同的配置,切换引脚的实际功能。通用 I/O 口主要特性如下:
输入输出模式可控制。
– 输出模式一般包括:推挽、开漏、上拉、下拉。引脚为输出模式时,可以通过配置引脚输出的电
平状态为高电平或低电平来控制连接的外围设备。
– 输入模式一般包括:浮空、上拉、下拉、模拟。引脚为输入模式时,可以读取引脚的电平状态,
即高电平或低电平。
常见的库函数:
- rt_device_register 注册设备
- rt_device_unregister 取消注册设备
- device_init: //初始化函数
- device_open: //打开设备
- device_close://关系设备
- device_read // 读取设备
- device_write // 写入设备
- device_control // 控制设备
所以常规操作:注册设备、初始化设备、打开设备。根据输入还是输出,选择对应函数,最后完成后关闭设备。
应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件。I/
O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示:
7.2 加载device库
勾选device库
找到rtconfig.h文件,使能rt-device
7.3 PIN设备的输出口
7.3.1 配置led驱动文件,修改led.c和led.h 文件
修改 led.c 文件,配置输入模式
rt_err_t LED_Init(rt_device_t dev)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); // APB
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; // ????
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; // ????
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed=GPIO_Low_Speed;
GPIO_Init(GPIOF,&GPIO_InitStructure);
GPIO_SetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
return RT_EOK;
}
rt_err_t LED_Ctr(rt_device_t dev, int name, void *sta)
{
LED_Status *status =(LED_Status *)sta;
switch(name)
{
case led0:
{
if(*status == led_on) GPIO_ResetBits(GPIOF,GPIO_Pin_9);
else GPIO_SetBits(GPIOF,GPIO_Pin_9);
break;
}
case led1:
{
if(*status == led_on) GPIO_ResetBits(GPIOF,GPIO_Pin_10);
else GPIO_SetBits(GPIOF,GPIO_Pin_10);
break;
}
default: break;
}
return RT_EOK;
}
修改 led.h,加载模式
#ifndef __LED_H
#define __LED_H
#include "stm32f4xx.h"
#include "rtthread.h"
typedef enum
{
led_on,
led_off
}LED_Status;
typedef enum
{
led0,
led1,
}LED_Name;
rt_err_t LED_Init(rt_device_t dev);
rt_err_t LED_Ctr(rt_device_t dev, int name, void *sta);
#endif
7.3.2 注册设备
在board.c 里面写入下面的文件
rt_device_t led;
int Board_Init(void) //
{
led=rt_device_create(RT_Device_Class_Char,1);
led->init=LED_Init;
led->control=LED_Ctr;
led->open=RT_NULL;
led->close=RT_NULL;
led->read=RT_NULL;
led->write=RT_NULL;
rt_device_register(led,"led",RT_DEVICE_FLAG_WRONLY); //
rt_device_init(led);
return RT_EOK;
}
INIT_DEVICE_EXPORT(Board_Init);
7.3.3 新建一个线程,操作灯亮和灯灭
static void user_cmd(int argc, char**argv) // shell_cmd led on
{
if(argc!=3)
{
rt_kprintf("Please input'shell_cmd led <on|off>'\n");
}
else
{
rt_device_t dev= rt_device_find(argv[1]); // ????
if(dev==RT_NULL)
rt_kprintf("Please input user_cmd led <on|off>'\n");
else
{
rt_err_t err=rt_device_open(dev,RT_DEVICE_OFLAG_WRONLY); // ???????
if(err==RT_EOK)
{
LED_Status sta;
if( !rt_strcmp(argv[2],"on"))
sta=led_on;
else
sta=led_off;
rt_device_control(dev,led0,&sta);
rt_device_close(dev);
}
else
{
rt_kprintf("open device fail\r\n");
}
}
}
}
MSH_CMD_EXPORT(user_cmd, user cmd: user_cmd led <on|off>);
输入对应指令, 等待系统返回结果
user_cmd led0 on
7.4 PIN设备的输入口
7.4.1 修改驱动按键驱动文件
修改key.c 文件
rt_err_t Key_Init(rt_device_t dev)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; // ????
GPIO_InitStructure.GPIO_Speed=GPIO_Low_Speed;
GPIO_Init(GPIOE,&GPIO_InitStructure);
}
rt_size_t Key_Read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
rt_size_t ops;
Key_Status *KeyBuff=buffer;
switch(pos) // 位置
{
case key0:
{
if( GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == RESET)
{
KeyBuff[pos]=press;
}
else
{
KeyBuff[pos]=unpress;
}
ops=1;
}
break;
case key1:
{
if( GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == RESET)
{
KeyBuff[pos]=press;
}
else
{
KeyBuff[pos]=unpress;
}
ops=1;
}
break;
case key2:
{
if( GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) == RESET)
{
KeyBuff[pos]=press;
}
else
{
KeyBuff[pos]=unpress;
}
ops=1;
}
break;
default:
ops=-1;
break;
}
return ops;
}
修改key.h文件
#ifndef __KEY_H
#define __KEY_H
#include "stm32f4xx.h"
#include "rtthread.h"
typedef enum
{
unpress,
press,
}Key_Status;
typedef enum
{
key0,
key1,
key2,
}Key_Name;
rt_err_t Key_Init(rt_device_t dev);
rt_size_t Key_Read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
#endif
7.4.2 注册设备
注册按键
rt_device_t led,key;
void Board_Init(void) //
{
led->init=LED_Init;
led->control=LED_Ctr;
led->open=RT_NULL;
led->close=RT_NULL;
led->read=RT_NULL;
led->write=RT_NULL;
rt_device_register(led,"led",RT_DEVICE_FLAG_WRONLY); //
rt_device_init(led);
key->init=Key_Init;
key->control=RT_NULL;
key->open=RT_NULL;
key->close=RT_NULL;
key->read=Key_Read;
key->write=RT_NULL;
rt_device_register(key,"key",RT_DEVICE_FLAG_RDONLY); //
rt_device_init(key);
}
7.4.3 新建一个线程,获取按键状态
static void user_cmd(int argc, char**argv) // shell_cmd led on
{
if(argc<2)
{
rt_kprintf("Please input'shell_cmd led <on|off>'\n");
}
else
{
rt_device_t dev= rt_device_find(argv[1]); // 获取句柄
if(dev==RT_NULL)
rt_kprintf("Please input'shell_cmd <led|key> <on|off>'\n");
else
{
if(!rt_strcmp(argv[1],"led")&&(argc==3))
{
rt_err_t err=rt_device_open(dev,RT_DEVICE_OFLAG_WRONLY); // 以写入方式打开
if(err==RT_EOK)
{
LED_Status sta;
if( !rt_strcmp(argv[2],"on"))
sta=led_on;
else
sta=led_off;
rt_device_control(dev,led0,&sta);
rt_device_close(dev);
}
else
{
rt_kprintf("open device fail\r\n");
}
}
else if(!rt_strcmp(argv[1],"key") &&(argc==2))
{
rt_err_t err=rt_device_open(dev,RT_DEVICE_OFLAG_RDONLY);
Key_Status key;
if(err==RT_EOK)
{
if( rt_device_read(dev,key0,&key,1)==1)
{
if(key==press)
rt_kprintf("key0 is press\r\n");
}
rt_device_close(dev);
}
else
{
rt_kprintf("open device fail\r\n");
}
}
}
}
}
MSH_CMD_EXPORT(user_cmd, user cmd: user_cmd <led|key> <on|off>);
7.5 总结
由于NANO版本的PIN很弱的,所以最好使用标准版来开发设备的开发。
所以在开发设备的流程很对应的驱动函数很简单,可以作为基本的入门介绍。了解这个设备操作流程。也是对后面学习LINUX的设备驱动开发有几个概念。
我们接下来进行图形界面开发介绍,GUI模块也是很重要的模块。