目录
驱动程序
应用程序
控制硬件设备步骤
找出硬件设备所对应的设备节点文件
找出驱动程序规定的设备文件使用方式
I.MX 6ULL开发板
LED控制
蜂鸣器控制
按键输入检测
树莓派GPIO
树莓派点亮LED的多种方式
UART
为硬件设备创建相应的设备节点文件。创建设备文件时,规定好设备文件的使用方式。
根据驱动程序规定的设备文件使用方式去控制硬件。
/dev目录下:对驱动程序熟悉的工程师可以使用,一个设备节点文件控制硬件全部特性。
/sys目录下:业余工程师使用,一个设备节点文件只控制硬件的一个特性。严格来说,它下面的文件是Linux内核导出到用户空间的硬件操作接口。
Linux系统引脚编号规则(针对i.MX6ull):(组号-1)*32+组内引脚编码。
例如GPIO1_19,组号为1,组内引脚编码为19。所有GPIO1_19再Linux内核的引脚编号为19。
/sys/class/gpio/export:导出GPIO子系统硬件操作接口。
/sys/class/gpio/gpio19/direction:控制芯片引脚的输入输出模式。in代表输入,out代表输出。
/sys/class/gpio/gpio19/value:控制芯片引脚的输出电平。1代表高电平,0代表低电平。
sudo echo 19 > /sys/class/gpio/export
sudo sh -c 'sudo echo out > /sys/class/gpio/gpio19/direction'
sudo sh -c 'sudo echo 1 > /sys/class/gpio/gpio19/value'
led.c
#include
#include
#include
#include
#include
#include
//ARM 开发板LED设备的路径
#define RLED_DEV_PATH "/sys/class/leds/red/brightness"
#define GLED_DEV_PATH "/sys/class/leds/green/brightness"
#define BLED_DEV_PATH "/sys/class/leds/blue/brightness"
int main(int argc, char* argv[])
{
int red_fd, green_fd, blue_fd;
int res = 0;
printf("This is the led demo\n");
//获取红灯的设备文件描述符
red_fd = open(RLED_DEV_PATH, O_WRONLY);
if(r_fd < 0)
{
printf("Fail to Open %s device\n", RLED_DEV_PATH);
exit(1);
}
//获取绿灯的设备文件描述符
green_fd = open(GLED_DEV_PATH, O_WRONLY);
if(r_fd < 0)
{
printf("Fail to Open %s device\n", GLED_DEV_PATH);
exit(1);
}
//获取蓝灯的设备文件描述符
blue_fd = open(BLED_DEV_PATH, O_WRONLY);
if(r_fd < 0)
{
printf("Fail to Open %s device\n", BLED_DEV_PATH);
exit(1);
}
while(1)
{
write(red_fd, "255", 3);
sleep(1);
write(red_fd, "0", 1);
write(green_fd, "255", 3);
sleep(1);
write(green_fd, "0", 1);
write(blue_fd, "255", 3);
sleep(1);
write(blue_fd, "0", 1);
}
}
beep.h
#ifndef _BSP_BEEP_H
#define _BSP_BEEP_H
//蜂鸣器的GPIO引脚号
//i.mx 6ull 计算方式:GPIOn_IOx = (n-1)*32 + x
//如GPIO1_IO19 = (1-1)*32 + 19 = 19
#define BEEP_GPIO_INDEX "19"
/**
* @brief 初始化蜂鸣器GPIO
* @return
* @arg 0:正常
* @arg 1:export文件打开错误
* @arg 2:direction文件打开错误
**/
extern int beep_init(void);
/**
* @brief 关闭蜂鸣器GPIO的export输出
* @return
* @arg 0:正常
* @arg !0:value文件打开错误
**/
extern int beep_deinit(void);
/**
* @brief 蜂鸣器响
* @return
* @arg 0:正常
* @arg !0:value文件打开错误
**/
extern int beep_on(void);
/**
* @brief 关闭蜂鸣器GPIO的export输出
* @return
* @arg 0:正常
* @arg !0:unexport文件打开错误
**/
extern int beep_off(void);
#endif
beep.c
#include
#include
#include
#include
#include
#include "../include/beep.h"
int beep_init(void)
{
int fd;
char gpio_path_direction[40] = {0};
/*******************索引配置********************/
fd = open("/sys/class/gpio/export", O_WRONLY);
if(fd < 0)
return 1;
write(fd, BEEP_GPIO_INDEX, strlen(BEEP_GPIO_INDEX));
close(fd);
/*******************索引配置********************/
/*******************方向配置********************/
sprintf(gpio_path_direction, "/sys/class/gpio/gpio%d/direction", BEEP_GPIO_INDEX);
fd = open(gpio_path_direction, O_WRONLY);
if(fd < 0)
return 2;
write(fd, "out", strlen("out"));
close(fd);
/*******************方向配置********************/
return 0;
}
int beep_deinit(void)
{
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if(fd < 0)
return 1;
write(fd, BEEP_GPIO_INDEX, strlen(BEEP_GPIO_INDEX));
close(fd);
return 0;
}
int beep_on(void)
{
int fd;
char gpio_value[40] = {0};
sprintf(gpio_value, "/sys/class/gpio/gpio%d/value", BEEP_GPIO_INDEX);
fd = open(gpio_value, O_WRONLY);
if(fd < 0)
return 1;
write(fd, "1", 1);
close(fd);
}
int beep_off(void)
{
int fd;
char gpio_value[40] = {0};
sprintf(gpio_value, "/sys/class/gpio/gpio%d/value", BEEP_GPIO_INDEX);
fd = open(gpio_value, O_WRONLY);
if(fd < 0)
return 1;
write(fd, "0", 1);
close(fd);
rerurn 0;
}
main.c
#include
#include
#include
#include
#include
#include "../include/beep.h"
int main(int argc, char* argv[])
{
int res = 0;
char buf[10];
printf("This is the beep demo\n");
res = beep_init();
if(res)
{
printf("beep init error, code = %d\n", res);
return 0;
}
while(1)
{
printf("please input the value: 0--off 1--on q--exit\n");
scanf("%10s", buf);
switch(buf[0])
{
case '0':
beep_off();
break;
case '1':
beep_on();
break;
case 'q':
beep_deinit();
printf("Exit\n");
return 0;
default:
break;
}
}
}
key按键的设备文件:/dev/input/by-path/platform-gpio-keys-event
struct input_event
{
struct timeval time; //记录输入时间的时间戳
__u16 type; //记录输入事件的类型,比如按键输入、坐标输入、特殊类型(EV_SYN,同步事件,提醒我们及时处理已经发生的完成输入事件)
__u16 code; //记录输入类型的具体事件代号,比如键盘发生按键输入类型事件时,记录键盘哪个值被按下
__s32 value; //记录事件的具体值,比如按键输入类型事件里,value--1表示按键被按下,value--0表示按键被弹起
};
input子系统:按键、键盘、鼠标、触摸屏等。
main.c
#include
#include
#include
#include
#include
#include
#include
#include
//开发板的KEY按键,请根据实际情况修改
const char default_path[] = "/dev/input/by-path/platform-gpio-keys-event";
int main(int argc, char* argv[])
{
int fd;
struct input_event event;
char *path;
printf("This is a input device demo\n");
//若无输入参数则使用默认事件设备
if(argc > 1 )
path = argv[1];
else
path = (char *)default_path;
fd = open(path, O_RDONLY);
if(fd < 0)
{
printf("Fail to open device:%s\n", path);
exit(1);
}
printf("Test device:%s\n", input);
while(1)
{
if( read(fd, &event, sizeof(event)) == sizeof(event))
{
//EV_SYN是事件分隔标志,不打印
if(event.type != EV_SYN)
{
printf("Event:time %ld.%ld,type %d,code %d,value %d\n",
event.time.tv_sec, event.time.tv_usec,
event.type,
event.code,
event.value);
}
}
}
close(fd);
return 0;
}
树莓派的GPIO引脚为40个,树莓派的引脚共有三种编码:板载编码、BCM编码、Wiring编码。
板载编码:按照树莓派主板上引脚排针编号顺序排序1~40号引脚。
BCM编码:python语言常用编码。
Wiring编码:c语言常用编码。
终端查看引脚编码:pinout-查看板载编码。gpio readall-查看全部编码。
//支持gpio readall
cd /tmp
wget https://project-downloads.drogon.net/wiringpi-latest.deb
sudo dpkg -i wiringpi-latest.deb
gpio -g mode 4 out //设置管脚为输出模式,-g表示是以BCM编码为编码方式。默认为wiringPi编码方式
gpio -g read 4 //读取管脚当前状态
gpio -g write 4 1 //设置管脚为高电平
gpio -g write 4 0 //设置管脚为低电平
方式一是直接通过终端命令进行控制,从内核空间找到引脚直接控制GPIO引脚。
//选定GPIO目录
cd /sys/class/gpio
ls
echo BCM引脚编码 > export //GPIO操作接口从内核空间暴露到用户空间,执行后该目录会增加一个引脚文件
ls //加入多了gpio26
//使用GPIO开始控制
cd gpio26
echo out > diretion
echo 1 > value
echo 0 > value
//注销GPIO引脚
cd ..
echo 26 > unexport //注销GPIO26接口
方式二是使用Python来控制LED灯,使用RPi.GPIO库。新建文件vim led.py。
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM) //采用BCM编号方式
GPIO.setup(26,GPIO.OUT) //指定使用26号端口
for i in range(1,10):
GPIO.output(26,GPIO.HIGH)
sleep(1)
GPIO.cleanup()
python3 led.py执行程序。
方式三是使用C来控制LED灯,使用wiringPi.h库。新建文件vim led.c。
#include
#define Pin 25
int main()
{
if(wiringPiSetup()<0)
return 1;
pinMode(Pin,OUTPUT);
for(int i=0;i<10;i++)
{
digitalWrite(Pin,1);
delay(200);
digitalWrite(Pin,0);
delay(200);
}
return 0;
}
gcc -o led led.c -lwiringPi
sudo ./led
硬件串口:/dev/ttyAMA0。
mini串口:/dev/ttyS0。
主UART:可以通过引脚实现通讯。/dev/serial0。
辅助UART:不能使用GPIO引脚通讯,默认被分配给蓝牙端。/dev/serial1。
默认情况下,mini串口是禁用的,无论将它指定为主UART还是辅助UART。在树莓派上,TxD和RxD是作为主串口的收发端。
串口通讯步骤
第一步是准备待调试的硬件串口。将硬件串口设置为主串口。因为主串口可以使用引脚通讯。
输入ls /dev -al查看串口打开情况。只有serial1 -> ttyAMA0 ,没找到serial0。
然后打开mini UART。命令行可输入sudo raspi-config进入树莓派配置,Interfacing Options是接口选项菜单,可启用/禁用:Camera、SSH、VNC、SPI、I2C、Serial、1-Write和远程访问GPIO。我们Enable Serial Port,Disable Serial Console。完成更改后选中Finish按钮,系统会询问是否重新启动,选择重新启动。
输入ls /dev -al查看串口打开情况。有serial1 -> ttyAMA0 ,serial0 -> ttyS0。
然后将硬件串口设置为主串口。命令行输入sudo vim /boot/config.txt打开文件,在末行添加:
dtoverlay=pi3-miniuart-bt
force_turbo=1
reboot重启生效。输入ls /dev -al查看串口打开情况。有serial0 -> ttyAMA0 ,serial1 -> ttyS0。
第二步是树莓派安装minicom串口助手。
输入sudo apt-get install minicom。
第三步是PC端安装串口调试工具。使用USB转TTL工具连接电脑和树莓派,开始通讯。
第四步是输入minicom -D /dev/ttyAMA0 -b 9600