iMX287A多種方法實現流水燈效果
目錄
- 1.流水燈在電子電路中的地位
- 2.硬體電路分析
- 3.先點個燈吧
- 4.shell指令碼實現流水燈
- 5.ANSI C檔案操作實現流水燈
- 6.Linux 系統呼叫實現流水燈
@
1.流水燈在電子電路中的地位
記得第一次接觸微控制器時,還是用的AT89S52微控制器,第1個程式就是點亮一個LED,然後再實現LED流水燈的效果。在這個過程中,可以瞭解整個程式的結構,GPIO的使用,延時的使用等。可以說微控制器學習中的點燈,就相當於C語言中的"Hello World"!好的,我們來看一下在ARM Linux下如何控制GPIO。
2.硬體電路分析
點燈的根本是控制LED對應GPIO輸出高低電平,那要控制哪個GPIO呢?這就需要檢視原理圖,看LED是連線到了哪個GPIO管腳。
iMX287的擴充套件板AP-283Demo原理圖中,LED的驅動電路:
與聯結器的連線關係:
這裡我們把:4個LED的管腳和J8C的4個管腳使用跳線帽短接起來,即
J8A_1=LED1 -> J8C_1=IO3.26
J8A_2=LED2 -> J8C_2=IO3.22
J8A_3=LED3 -> J8C_3=IO3.20
J8A_4=LED4 -> J8C_4=IO2.7
即:
GPIO3_26 = LED1 GPIO3_22 = LED2 GPIO3_20 = LED3 GPIO2_7 = LED4
所以,我們只需要控制這幾個GPIO的高低電平,可以控制LED了。
連線方式:
3.先點個燈吧
iMX28 系列處理器的 IO 埠分為 7 個 BANK,其中 BANK0~4 具有 GPIO 功能,每個BANK 具有 32 個 I/O。iMX283 部分 BANK 的引腳不支援 GPIO 功能,具體需要參考《IMX28CEC_Datasheet.pdf》手冊。在匯出 GPIO 功能引腳時,需要先計算 GPIO 引腳的排列序號,其序號計算公式:
GPIO序號 = Bank * 32 + N #LED對應的序號 LED1 -> GPIO3_26 = 3 * 32 + 26 = 122 LED2 -> GPIO3_22 = 3 * 32 + 22 = 118 LED3 -> GPIO3_20 = 3 * 32 + 20 = 116 LED4 -> GPIO2_7 = 2 * 32 + 7 = 71
串列埠或SSH登入開發板之後,通過如下過程可匯出對應GPIO的功能,以LED1對應的122為例:
#進入到gpio驅動所在的資料夾
cd /sys/class/gpio
#生成LED1操作的相關檔案
echo 122 > export
#進入到LED1控制資料夾
cd gpio122
#設定GPIO為輸出方向
echo out > direction
#檢視當前GPIO的輸入輸出方向
cat direction
#設定GPIO輸出0點亮LED
echo 0 > value
#檢視當前GPIO的輸出狀態
cat value
#設定GPIO輸出1熄滅LED
echo 1 > value
#檢視當前GPIO的輸出狀態
在這裡,就體現了Linux系統下一切皆檔案的含義。
這樣,就實現了某個GPIO輸出高低電平的目的,其他LED的控制,只需要修改對應的序號即可。
如果想取消LED1的GPIO功能,可以通過如下命令實現:
echo 122 > unexport
當然,如果是讀取輸入,如按鍵等,就設定方向為in輸入,通過cat value
來讀取輸入的狀態。
4.shell指令碼實現流水燈
好了,知道了如何實現GPIO的控制,那麼如何方便、快捷的執行以上步驟呢?我們這裡先介紹一種通過shell指令碼的方式控制LED實現流水燈的效果:
#在home目錄新建led_blink.sh指令碼檔案
touch led_blink.sh
#輸入控制LED實現流水燈效果的程式碼
vi led_blink.sh
led_blink.sh的檔案內容:
#!/bin/bash
#LED對應的GPIO
led1_pin=122
led2_pin=118
led3_pin=116
led4_pin=71
#echo $led1_pin $led2_pin $led3_pin $led4_pin
#GPIO操作檔案如果不存在則建立,否則不建立
for pin in $led1_pin $led2_pin $led3_pin $led4_pin
do
#如果資料夾存在
if [ ! -d "/sys/class/gpio/gpio$pin/" ];
then
echo $pin > /sys/class/gpio/export
else
echo "$pin dir exist!"
fi
done
#GPIO方向設定為輸出
for pin in $led1_pin $led2_pin $led3_pin $led4_pin
do
echo out > /sys/class/gpio/gpio$pin/direction
done
#死迴圈
while true
do
#GPIO輸出0/1
for pin in $led1_pin $led2_pin $led3_pin $led4_pin
do
echo "點亮$pin"
echo 0 > /sys/class/gpio/gpio$pin/value
sleep 1
echo "熄滅$pin"
echo 1 > /sys/class/gpio/gpio$pin/value
done
done
儲存退出之後,通過如下命令執行這個指令碼:
#新增可執行許可權
chmod +x led_blink.sh
#執行指令碼檔案
./led_blink.sh
然後就可以看到流水燈效果了:
這種方式,沒什麼高深的技術,就是把之前通過一行一行的命令,轉換成了自動化的指令碼檔案。為了寫這種shell指令碼,需要學習一些基本的shell語法。下面我們來介紹兩種比較通用的方法,通過C語言檔案讀寫的方式來實現流水燈效果。
5.ANSI C檔案操作實現流水燈
只使用標準ANSI C檔案操作來實現流水燈效果。標準C語言操作,常用的函式有
fopen/fclose/fwrite/fread/fseek
等。無論是Linux還是Windows都是通用的。
/* ANSI C檔案操作實現流水燈 By whik */
//包含printf和sleep等
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LED1_PIN 122
#define LED2_PIN 118
#define LED3_PIN 116
#define LED4_PIN 71
#define GPIO_PATH "/sys/class/gpio/"
int main(void)
{
int led_table[4] = {LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN};
char path[100];
char cmd_export[100];
int i;
char *str;
FILE *fp; //檔案指標
int cnt_w; //寫入的位元組數
//判斷LED對應的操作資料夾是否存在,不存在自動建立
for(i = 0; i < 4; i++)
{
sprintf(path, "%sgpio%d", GPIO_PATH, led_table[i]); //sys/class/gpio/gpio122
if (!access(path, 0))
printf("%s 資料夾存在\n", path);
else
{
printf("%s 資料夾不存在\n", path);
sprintf(cmd_export, "echo %d > %sexport", led_table[i], GPIO_PATH);//echo 122 > /sys/class/gpio/export
system(cmd_export); //執行export命令
if(!access(path, 0)) //訪問資料夾,確認建立成功
printf("%s 匯出成功\n", path);
else
return -1;
}
}
//設定輸出方向
for(i = 0; i < 4; i++)
{
sprintf(path, "%sgpio%d/direction", GPIO_PATH, led_table[i]);
fp = fopen(path, "r+");
if(fp != NULL) //開啟成功
{
printf("%s 開啟成功\n", path);
cnt_w = fwrite("out", 3, 1, fp);
if(cnt_w == EOF)
{
printf("方向寫入失敗\n");
return -1;
}
else
printf("方向寫入成功\n");
fclose(fp); //檔案關閉
}
else
{
printf("%s 檔案開啟失敗\n", path);
return -1;
}
}
//流水燈效果
while(1)
{
for(i = 0; i < 4; i++)
{
sprintf(path, "%sgpio%d/value", GPIO_PATH, led_table[i]);
fp = fopen(path, "w+");
if(fp != NULL)
{
fwrite("0", 1, 1, fp);
fseek(fp, 0, SEEK_SET); //重新定位到檔案起始
printf("%s 寫入0\n", path);
sleep(1);
fwrite("1", 1, 1, fp);
printf("%s 寫入1\n", path);
fseek(fp, 0, SEEK_SET);
fclose(fp);
}
else
{
printf("%s 檔案開啟失敗\n", path);
}
}
}
return 0;
}
實現原理和之前的shell指令碼也是一樣的思路:
- 先判斷LED對應的GPIO操作檔案是否匯出,如果沒有匯出則執行匯出命令,否則不執行。
- 迴圈的方式,依次設定4個LED的方向為輸出
- 迴圈的方式,控制4個GPIO的輸出高低電平,外面是一個死迴圈
6.Linux 系統呼叫實現流水燈
Linux系統呼叫,常用的檔案IO操作函式有open/close/read/write/lseek/ulink
等。函式用法也很簡單,和標準C語言用法幾乎一樣,這裡不再介紹。
/* Linux 系統函式實現流水燈 */
//涉及到裝置操作的,需包含以下標頭檔案
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//包含close/read/write/lseek等函式
#include <unistd.h>
//包含printf/sprintf函式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LED1_PIN 122
#define LED2_PIN 118
#define LED3_PIN 116
#define LED4_PIN 71
#define GPIO_PATH "/sys/class/gpio/"
int main(void)
{
int led_table[4] = {LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN};
char path[100];
char cmd_export[100];
int i;
char *str;
int fd;
int cnt_w; //寫入的位元組數
//判斷LED對應的操作資料夾是否存在,不存在自動建立
for(i = 0; i < 4; i++)
{
sprintf(path, "%sgpio%d", GPIO_PATH, led_table[i]); //sys/class/gpio/gpio122
if (!access(path, 0))
printf("%s 資料夾存在\n", path);
else
{
printf("%s 資料夾不存在\n", path);
sprintf(cmd_export, "echo %d > %sexport", led_table[i], GPIO_PATH);//echo 122 > /sys/class/gpio/export
system(cmd_export); //執行export命令
if(!access(path, 0)) //訪問資料夾,確認建立成功
printf("%s 匯出成功\n", path);
else
return -1;
}
}
//設定輸出方向
for(i = 0; i < 4; i++)
{
sprintf(path, "%sgpio%d/direction", GPIO_PATH, led_table[i]);
fd = open(path, O_RDWR); //linux系統函式,失敗返回-1
if(fd != -1) //開啟成功
{
printf("%s 開啟成功\n", path);
cnt_w = write(fd, "out", 3); //linux系統函式
if(cnt_w <= 0)
{
printf("方向寫入失敗\n");
return -1;
}
else
printf("方向寫入成功\n");
close(fd); //linux系統函式,成功返回0
}
else
{
printf("%s 檔案開啟失敗\n", path);
return -1;
}
}
//流水燈效果
while(1)
{
for(i = 0; i < 4; i++)
{
sprintf(path, "%sgpio%d/value", GPIO_PATH, led_table[i]);
fd = open(path, O_RDWR); //linux系統函式
if(fd != -1)
{
write(fd, "0", 1);
lseek(fd, 0, SEEK_SET); //重新定位到檔案起始
printf("%s 寫入0\n", path);
sleep(1);
write(fd, "1", 1);
printf("%s 寫入1\n", path);
lseek(fd, 0, SEEK_SET);
close(fd);
}
else
{
printf("%s 檔案開啟失敗\n", path);
}
}
}
return 0;
}
實現過程也很簡單,就是對檔案的讀寫,Linux下一切皆檔案的含義你理解了嗎?
我的公眾號:mcu149