嵌入式系统应用-第七章 操作系统(RTOS)之PIN(GPIO)设备操作

本文详细介绍了RT-Thread操作系统中的PINGPIO设备操作,包括基本概念、Nano版本与标准版的区别,以及如何注册、初始化和操作GPIO设备,如LED和按键的驱动示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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库

在这里插入图片描述](https://img-blog.csdnimg.cn/3315f53d2a78487099499bbae7d8a302.png)

找到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模块也是很重要的模块。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数贾电子科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值