To access GPIO, the most intuitive way is to write a driver for this purpose. This approach is most generic, but the Linux driver model is complicated, It is requisite to spend time to learn that. Besides, in the modern operation systems, the drivers have been treated as the external parts of operation system. Applying a driver to the operation system means the operation system has been patched. If Somebody writes an inappropriate driver, something might be wrong but the system would keep working, it is very difficult to seek out the bug.
The goal is to access GPIO only, It is not necessary to be in anguish in coding a driver.
To eschew to write a driver but the accessing GPIO is possible, the most simple way is to exploit System file system, sysfs. That concept of sysfs is that everything is file under Linux, the accessing behaviors would correspond to the manipulations respectively.
My development board is openWRT system, which is :
root@GL-AR150:/tmp# cat /proc/cpuinfo system type : Atheros AR9330 rev 1 machine : TP-LINK TL-WR720N v3 processor : 0 cpu model : MIPS 24Kc V7.4
零. Control Existing LED.
In my development board, the folder, /sys is where sysfs existing. The LEDs under the path :
root@GL-AR150:/tmp# ls /sys/class/leds/ ath9k-phy0 tp-link:blue:eth1 tp-link:blue:wlan tp-link:blue:eth0 tp-link:blue:system
There are file LEDs under the path, but for my board, the physical LEDs are only four. the ath9k-phy0 does not exist.
Adopt the follow command to turn on the specific LED :
root@GL-AR150:/tmp# echo 1 > /sys/class/leds/tp-link\:blue\:wlan/brightness
The the line to turn off the LED:
root@GL-AR150:/tmp# echo 0 > /sys/class/leds/tp-link\:blue\:wlan/brightness
Query the LED current status:
root@GL-AR150:/tmp# cat /sys/class/leds/tp-link\:blue\:wlan/brightness 1
1 represents bright, 0 is dim, it is very natural.
the C code for corresponding to above action is :
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <fcntl.h> #define BUFFER_MAX (8) int SetSysfsLEDStatus(char *p_led_sysfs_path, int status) { char status_str[BUFFER_MAX]; char led_sysfs_location[PATH_MAX]; int sysfs_led_fd; memset(&led_sysfs_location[0], 0, PATH_MAX); strncpy(&led_sysfs_location[0], p_led_sysfs_path, PATH_MAX); { int path_len; path_len = strlen(led_sysfs_location); if( '/'== led_sysfs_location[path_len - 1]) strncat(&led_sysfs_location[path_len - 1], "/brightness", PATH_MAX); else strncat(&led_sysfs_location[0], "/brightness", PATH_MAX); } sysfs_led_fd = open(&led_sysfs_location[0], O_RDWR); if (-1 == sysfs_led_fd) { printf("Failed to open %s for writing!\n", &led_sysfs_location[0]); return -1; }/*file description*/ { char current_status[BUFFER_MAX]; if(0 > read(sysfs_led_fd, ¤t_status[0], BUFFER_MAX)) { printf("read current status fail\r\n"); close(sysfs_led_fd); return -2; } printf("current status = %s\r\n", ¤t_status[0]); } switch(status) { case 0: snprintf(&status_str[0], BUFFER_MAX, "0"); break; case 1: snprintf(&status_str[0], BUFFER_MAX, "1"); break; default: return -2; }/*switch value*/ if (1 != write(sysfs_led_fd, &status_str[0], strlen(status_str))) { printf("Failed to write value!\n"); close(sysfs_led_fd); return -3; }/*if write*/ close(sysfs_led_fd); printf("status %d has been set \r\n", status); return 0; }/*SetSysfsLEDStatus*/ int main(int argc, char *argv[]) { int k; if(3 > argc) { printf("not enough argments\r\n" "\tit should be %s sysfs_led_path status\r\n", argv[0]); return -1; } SetSysfsLEDStatus(argv[1], '0'== argv[2][0] ? 0 : 1); return 0; }/*main*/
And the Makefile be :
OPENWRT_ROOT=/home/gaiger/openwrt-cc/staging_dir OPENWRT_TOOLCHAIN_PATH = $(OPENWRT_ROOT)/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2 CC = $(OPENWRT_TOOLCHAIN_PATH)/bin/mips-openwrt-linux-gcc CFLAGS := -g all: $(CC) $(CFLAGS) main.c -o sysfs_led_control clean: rm sysfs_led_control -f
The Makefile is just to be Specified the cross-compiler location only.
After Cross-compiling (It should be called parasitic compiling) and run the binary :
root@GL-AR150:/tmp# ./sysfs_led_control /sys/class/leds/tp-link\:blue\:wlan/ 1 current status = 0 status 1 has been set root@GL-AR150:/tmp# ./sysfs_led_control /sys/class/leds/tp-link\:blue\:wlan/ 0 current status = 1 status 0 has been set
To here, You should know how to control the existing LEDs.
一. Connect a External LED in the preserved GPIO interface and control it.
It is demonstrate how to output a signal via GPIO.
Inquire the circuit diagram, there are GPIOs preserved for the user :
The pin consponding to the diagram is near ethernet lan port :
I choose GPIO20 to connect a LED ( with a protective resistor in 470 Ohm to avoid burning out).
that is :
the resistor value is not confined as 470 Ohm only, Any resistor between 200 to 2000k Ohm could be adopted to protect the LED.
After the connection has been set, type the following command in the terminal:
root@GL-AR150:/tmp# echo 20 > /sys/class/gpio/export root@GL-AR150:/tmp# echo out > /sys/class/gpio/gpio20/direction root@GL-AR150:/tmp# echo 1 > /sys/class/gpio/gpio20/value root@GL-AR150:/tmp# echo 0 > /sys/class/gpio/gpio20/value root@GL-AR150:/tmp# echo 20 > /sys/class/gpio/unexport
Theoretically, the external LED would be bright then dim.
The C code achieves the same goal be :
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <fcntl.h> #include <signal.h> #define IN (0) #define OUT (1) #define BUFFER_MAX (8) int GPIOExport(int pin) { char buffer[BUFFER_MAX]; ssize_t bytes_written; int fd; fd = open("/sys/class/gpio/export", O_WRONLY); if (-1 == fd) { fprintf(stderr, "Failed to open export for writing!\n"); return -1; }/*file description*/ bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); bytes_written = write(fd, buffer, bytes_written); close(fd); return 0; }/*GPIOExport*/ int GPIOUnexport(int pin) { char buffer[BUFFER_MAX]; ssize_t bytes_written; int fd; fd = open("/sys/class/gpio/unexport", O_WRONLY); if (-1 == fd) { printf("Failed to open unexport for writing!\n"); return -1; }/*file description*/ bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); write(fd, buffer, bytes_written); close(fd); return(0); }/*GPIOUnexport*/ int GPIODirection(int pin, int dir) { char file_location[PATH_MAX]; char direction_str[BUFFER_MAX]; int fd; snprintf(&file_location[0], PATH_MAX, "/sys/class/gpio/gpio%d/direction", pin); fd = open(&file_location[0], O_WRONLY); if (-1 == fd) { printf("Failed to open gpio direction for writing!\n"); return -1; }/**/ switch(dir) { case IN: snprintf(&direction_str[0], BUFFER_MAX, "in"); break; case OUT: snprintf(&direction_str[0], BUFFER_MAX, "out"); break; default: return -2; }/*switch dir*/ if(-1 == write(fd, &direction_str[0], strlen(direction_str))) { close(fd); printf("Failed to set direction!\n"); return -3; }/*if write*/ close(fd); return 0; }/*GPIODirection*/ int GPIOWrite(int pin, int value) { char values_str[BUFFER_MAX]; char file_location[PATH_MAX]; int fd; snprintf(&file_location[0], PATH_MAX, "/sys/class/gpio/gpio%d/value", pin); fd = open(&file_location[0], O_WRONLY); if (-1 == fd) { printf("Failed to open gpio value for writing!\n"); return -1; }/*file description*/ switch(value) { case 0: snprintf(&values_str[0], BUFFER_MAX, "0"); break; case 1: snprintf(&values_str[0], BUFFER_MAX, "1"); break; default: return -2; }/*switch value*/ if (1 != write(fd, &values_str[0], strlen(values_str))) { printf("Failed to write value!\n"); close(fd); return -2; }/*if write*/ close(fd); return 0; }/*GPIOWrite*/ int g_gpio_number; void InterruptSignalHandlingRoutine(int sig) { GPIOWrite(g_gpio_number, 0); GPIOUnexport(g_gpio_number); exit(0); }/*InterruptSignalHandlingRoutine*/ int main(int argc, char *argv[]) { int k; signal(SIGINT, InterruptSignalHandlingRoutine); if(2 > argc) { printf("%s should be followed by led_gpio_pin_number\r\n", argv[0]); return -1; } { char *p_temp; g_gpio_number = strtol(argv[1], &p_temp, 10); if(argv[1]== p_temp) { printf("%s is not a number for specifying gpio pin number\r\n", argv[1]); return -2; }/*not a number*/ }/*local variable*/ GPIOExport(g_gpio_number); GPIODirection(g_gpio_number, OUT); k = 0; while(k < 100*2) { GPIOWrite(g_gpio_number, (k + 1)&0x01); usleep(500*1000); k++; } GPIOUnexport(g_gpio_number); return 0; }/*main*/
The Makefile is almost the same, replace the sysfs_led_control as gpio_led_control.
Build and Execute the binary, you will find the external LED blinking per 0.5 seconds.
二. Connect a External push button in the preserved GPIO interface and read the pressed/released signal.
I adopt the GPIO 19 for connecting the push button.
It is to explain how to read d input signal.
you need to know what is pull-up and pull-down.
Pull-down : when the GPIO is dangling (does not be made as a circuit loop), The voltage in it should be low as ground and the value is 0; once the GPIO has been connected to Vcc, the voltage would be high( of course), and the value become 1.
Pull-up : contrast to Pull-down, the GPIO voltage should be high, and its value is 0; While the GPIO is been connected to ground, the value be 1.
Summary : Pull-down : 0 is low, 1 is high; Pull-up : 0 is high, 1 is low.
root@GL-AR150:/tmp# echo 19 > /sys/class/gpio/export root@GL-AR150:/tmp# echo in > /sys/class/gpio/gpio19/direction root@GL-AR150:/tmp# cat /sys/class/gpio/gpio19/active_low 0
It is pull-up mechanism for triggering . (active low false, means high is actived, it is high is 1 )
The value could be set, But I do not change that.
The circuit for me be :
I use the right one (pulldown), and the resistor in here is 10k Ohm.
You could use the command to enquire value :
root@GL-AR150:/tmp# cat /sys/class/gpio/gpio19/value #button released 0 root@GL-AR150:/tmp# cat /sys/class/gpio/gpio19/value #button pressed 1
The C code be :
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <linux/limits.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <signal.h> #define LOCAL static #define IN (0) #define OUT (1) #define BUFFER_MAX (8) LOCAL int GPIOExport(int pin) { char buffer[BUFFER_MAX]; ssize_t bytes_written; int fd; fd = open("/sys/class/gpio/export", O_WRONLY); if (-1 == fd) { fprintf(stderr, "Failed to open export for writing!\n"); return -1; }/*file description*/ bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); bytes_written = write(fd, buffer, bytes_written); close(fd); return 0; }/*GPIOExport*/ LOCAL int GPIOUnexport(int pin) { char buffer[BUFFER_MAX]; ssize_t bytes_written; int fd; fd = open("/sys/class/gpio/unexport", O_WRONLY); if (-1 == fd) { printf("Failed to open unexport for writing!\n"); return -1; }/*file description*/ bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); write(fd, buffer, bytes_written); close(fd); return(0); }/*GPIOUnexport*/ LOCAL int GPIODirection(int pin, int dir) { char file_location[PATH_MAX]; char direction_str[BUFFER_MAX]; int fd; snprintf(&file_location[0], PATH_MAX, "/sys/class/gpio/gpio%d/direction", pin); fd = open(&file_location[0], O_WRONLY); if (-1 == fd) { printf("Failed to open gpio direction for writing!\n"); return -1; }/**/ switch(dir) { case IN: snprintf(&direction_str[0], BUFFER_MAX, "in"); break; case OUT: snprintf(&direction_str[0], BUFFER_MAX, "out"); break; default: return -2; }/*switch dir*/ if(-1 == write(fd, &direction_str[0], strlen(direction_str))) { close(fd); printf("Failed to set direction!\n"); return -3; }/*if write*/ close(fd); return 0; }/*GPIODirection*/ #include <errno.h> LOCAL int GPIORead(int pin, int *p_value) { char buffer[BUFFER_MAX]; char file_location[PATH_MAX]; int read_size; int gpio_fd; int previous_button_status, current_button_status; char *p_tmp; snprintf(&file_location[0], PATH_MAX, "/sys/class/gpio/gpio%d/value", pin); gpio_fd = open(&file_location[0], O_RDONLY); if (-1 == gpio_fd) { printf("Failed to open gpio value for writing!\n"); return -1; }/*file description*/ lseek(gpio_fd, 0, SEEK_SET); read_size = read(gpio_fd, &buffer[0], 1); if(0 > read_size) { printf("Failed to read value!\n"); printf("error message = %s\r\n", strerror(errno)); close(gpio_fd); return -2; }/*if write*/ previous_button_status = strtol(&buffer[0], &p_tmp, 10); while(1) { usleep(15*1000); lseek(gpio_fd, 0, SEEK_SET); read_size = read(gpio_fd, &buffer[0], 1); if(0 > read_size) { printf("Failed to read value!\n"); printf("error message = %s\r\n", strerror(errno)); close(gpio_fd); return -2; }/*if write*/ current_button_status = strtol(&buffer[0], &p_tmp, 10); if(previous_button_status == current_button_status) continue; break; }; close(gpio_fd); *p_value = current_button_status; return 0; }/*GPIOWrite*/ int g_gpio_number; void InterruptSignalHandlingRoutine(int sig) { GPIOUnexport(g_gpio_number); exit(0); }/*InterruptSignalHandlingRoutine*/ int main(int argc, char *argv[]) { signal(SIGINT, InterruptSignalHandlingRoutine); if(2 > argc) { printf("%s should be followed by led_gpio_pin_number\r\n", argv[0]); return -1; } { char *p_temp; g_gpio_number = strtol(argv[1], &p_temp, 10); if(argv[1]== p_temp) { printf("%s is not a number for specifying gpio pin number\r\n", argv[1]); return -2; }/*not a number*/ }/*local variable*/ GPIOExport(g_gpio_number); GPIODirection(g_gpio_number, IN); while(1) { int is_pressed; GPIORead(g_gpio_number, &is_pressed); if(0 == is_pressed) printf("button released\r\n"); else printf("button pressed\r\n"); } GPIOUnexport(g_gpio_number); return 0; }/*main*/
I name the binary as gpio_button_status, build and run it :
root@GL-AR150:/tmp# ./gpio_button_status 19 button pressed button released button pressed button released
the print would follow the button status.
Now you know the sysfs is very useful for consigning/receiving signal to/from GPIO. But the sysfs is suited for low speed signal only. LOW SPEED means the speed in the order of KHz, or in the order of milli-second. The sysfs is not able to deal with signal speed in order of micro-second. it is pity that the most sensors ( besides the buttons) signal speed are in the scale of micro-second.
Reference :
http://blog.sina.com.cn/s/blog_7880d3350102w2um.html
https://developer.ridgerun.com/wiki/index.php/How_to_use_GPIO_signals
沒有留言:
張貼留言