1. 程式人生 > >iMX287A多種方法實現流水燈效果

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