1. 程式人生 > 其它 >一.LED燈的點亮——從簡單的彙編開始瞭解

一.LED燈的點亮——從簡單的彙編開始瞭解

首先,我們一步步從彙編開始點亮板子上的LED燈!

為什麼要先從彙編開始呢?一般來說我們只用C寫程式就行了,但是系統上電以後要對SOC的外設、DDR進行初始化(我們用的是I.MX6U,不需要這一步),設定堆疊指標(一般指向DDR),設定好C語言的環境關閉看門狗等流程,但這些功能用C是無法實現的,必須通過彙編來做。好在只需要簡單幾個指令就可以了。

下面我們從開發板的LED硬體來分析下原理:

先看下硬體原理圖:

燈通過3.3V供電,經過510歐的限流電阻和控制口LED0相連,通過搜尋查詢,我們發現LED0節點和GPIO_3相連(應該是GPIO1_3)

以前搞過STM32的夥伴們應該知道,要對IO進行初始化,需要下面的流程:

  1. 使能GPIO時鐘
  2. 設定IO複用,將其設定為GPIO
  3. 配置GPIO的電氣屬性
  4. 使用GPIO,輸出高低電平

對於IMX6ULL來說,流程基本是一樣的,我們一步一步來設定

一.使能GPIO時鐘:

查詢IMX6ULL的晶片手冊,CCM_CCGR0-6一共7個暫存器控制了6UL上所有的外設時鐘的使能(第18.6.23章 CCM Clock Gating Register 0 (CCM_CCGR0)),而gpio1對應的時鐘管理是歸屬於CCM_CCGR1的26、27兩個bit管理,要想使能這個時鐘,將其按照要求設定就可以了

設定值得要求

簡單化,只要把兩個位元值都置一就行了。更簡單,吧CCGR0-6都設為0xFFFFFFFF,相當於使能所有外設時鐘。

二.IO複用設定

複用設定的管理屬於IOMUX Controller管理,查詢晶片手冊,我們要設定的是GPIO1的IO3,要設定的是IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03(SW_MUX_CTL_PAD_GPIO1_IO03 SW MUX ControlRegister )

下面有個表是對其進行說明的

複用模式(MUX_MODE)一共有7種模式,對應3-0bit,我們是要使用GPIO功能,也就是對應0101=5。

三.設定電氣屬性

下面要對GPIO1_IO03的電氣屬性進行設定。可以查下目錄,管理其屬性的暫存器是IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03(32.6.156

SW_PAD_CTL_PAD_GPIO1_IO03 SW PAD Control Register)注意跟複用的區別,一個是PAD,一個是MUX

這個電氣屬性的內容比較多

Field Description

0 ODE_0_Open_Drain_Disabled — Open Drain Disabled

1 ODE_1_Open_Drain_Enabled — Open Drain Enabled

31–17
-

This field is reserved.
Reserved

16
HYS

Hyst. Enable Field
Select one out of next values for pad: GPIO1_IO03

0  HYS_0_Hysteresis_Disabled — Hysteresis Disabled
1  HYS_1_Hysteresis_Enabled — Hysteresis Enabled

15–14
PUS

Pull Up / Down Config. Field
Select one out of next values for pad: GPIO1_IO03

00  PUS_0_100K_Ohm_Pull_Down — 100K Ohm Pull Down
01  PUS_1_47K_Ohm_Pull_Up — 47K Ohm Pull Up
10  PUS_2_100K_Ohm_Pull_Up — 100K Ohm Pull Up
11  PUS_3_22K_Ohm_Pull_Up — 22K Ohm Pull Up

13
PUE

Pull / Keep Select Field

Select one out of next values for pad: GPIO1_IO03

0  PUE_0_Keeper — Keeper
1  PUE_1_Pull — Pull

12
PKE

Pull / Keep Enable Field
Select one out of next values for pad: GPIO1_IO02

0  PKE_0_Pull_Keeper_Disabled — Pull/Keeper Disabled
1  PKE_1_Pull_Keeper_Enabled — Pull/Keeper Enabled

11
ODE

Open Drain Enable Field
Select one out of next values for pad: GPIO1_IO02

10–8

-

This field is reserved.
Reserved
7–6
SPEED
Speed Field
Select one out of next values for pad: GPIO1_IO03
00  SPEED_0_low_50MHz_ — low(50MHz)
01  SPEED_1_medium_100MHz_ — medium(100MHz)
10  SPEED_2_medium_100MHz_ — medium(100MHz)
11  SPEED_3_max_200MHz_ — max(200MHz)
5–3
DSE

Drive Strength Field
Select one out of next values for pad: GPIO1_IO03

000  DSE_0_output_driver_disabled_ — output driver disabled;
001  R0(3.3V下為260Ω,1.8V下為150Ω,接DDR時為240Ω)
010  DSE_2_R0_2 — R0/2
011  DSE_3_R0_3 — R0/3
100  DSE_4_R0_4 — R0/4
101  DSE_5_R0_5 — R0/5
110  DSE_6_R0_6 — R0/6
111  DSE_7_R0_7 — R0/7

2–1
-
This field is reserved.
Reserved

0
SRE

Slew Rate Field
Select one out of next values for pad: GPIO1_IO02

0  SRE_0_Slow_Slew_Rate — Slow Slew Rate
1  SRE_1_Fast_Slew_Rate — Fast Slew Rate

也就是說暫存器IOMUSXC_SW_PAD_CTL_PAD_GPIO1_IO03是用來配置GPIO_IO03的,包括速度、驅動能力壓擺率等設定,下面挨個分析下這幾個引數

  • SRE(bit0):壓擺率,指IO電平反轉所需要的時間,時間越小波形就越抖,即壓擺率高。0的時候為低壓擺率,1時為高。當我們的IO做高速IO時要設定為1
  • DSE(bit5-3):當IO作為輸出時的驅動能力。共8個選項,0為關閉驅動,其餘對應表格查詢即可
  • SPEED(bit7-6):IO用作輸出時的IO速度,按需求依據表格設定
  • ODE(bit11):IO作為輸出時,是否使能開路輸出,0時禁止開路輸出,1時使能
  • PKE(bit12):用來使能或禁止上下拉、狀態保持器的功能:0時禁止上下拉/狀態保持器,1時使能
  • PUE(bit13):當IO為輸如的時候,用來設定IO使用上下拉還是狀態保持器:1時為上下拉,0時為狀態保持器,即外部電路斷電後此IO口可保持以前的狀態。
  • PUS(bit15-14):設定上下拉電阻阻值,按需設定
  • HYS(bit16):使能遲滯比較器,當IO作為輸入功能時有效,用於設定輸入接收器的施密特觸發器是否使能,如果需要對輸入波形進行整形的時候可以使能此位,0時禁止,1時使能

整個功能圖如圖所示

四.GPIO配置

這裡還有一個要配置是在STM32裡沒有的:配置GPIO,包含輸入/輸出,資料等等

手冊裡有一章是專門講GPIO的(Chapter28) ,查暫存器目錄發現有8個暫存器是我們需要設定的

GPIO data register(GPIOx_DR)

GPIO資料暫存器,一共32位,對應了IO的32個點,當GPIO配置為輸出到時候時,0為低電平,1為高電平。當GPIO為輸出到模式時,每個位對應一個GPIO,比方GPIO1_IO00引腳接地時,GPIO.DR的bit0值為0

GPIO direction register (GPIOx_GDIR)

方向暫存器(GPIO direction register (GPIOx_GDIR),28.5.2)

同樣也是32位,表明32個IO點工作的方向,0為輸入,1為輸出

我們需要的是將GPIO1_IO03設定為輸出模式,就要將GPIO1_GDIR的第3個bit置1

剩下的還有PSR,ICR1,ICR2,IMR,ISR,EDGE_SEL我們暫時用不到,這裡就先不講了,總之,GPIO的整體配置流程如下圖所示

程式碼編寫

這裡使用匯編來實現,組合語言的介紹我們放在另一章來講。反正這裡要用到就是讀取、寫入暫存器。

就按照前面的流程,先使能GPIO時鐘,這裡為了簡化操作,把所有Soc和GPIO1一個暫存器控制的所有外設都設為使能,也就是將CCM_CCGR1全都使能。

CCM_CCGR1的地址為20C_4000h base + 6Ch offset = 20C_406Ch(0x020c406c)

    /*使能所有外設時鐘 */
    ldr r0,=0x020c4068   @CCGR0
    ldr r1,=0xffffffff   @向CCGR0待寫入的資料
    str r1,[r0]          @將r1裡的資料寫入r0地址的暫存器

第二步就是設定複用,對應暫存器為IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03,地址為0x020e0068,應講MUX_MODE設定為ALT5

@設定複用IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 5
    ldr r0,=0x020e0068
    ldr r1,=0x5
    str r1,[r0]

第三步是設定電氣屬性,設定暫存器

IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 地址0x020E02F4,對應前面的分解說明,按照下面的要求設定
  • bit 16:0 HYS關閉
  • bit [15:14]: 00 預設下拉
  • bit [13]: 0 kepper功能
  • bit [12]: 1 pull/keeper使能
  • bit [11]: 0 關閉開路輸出
  • bit [10-8]: 000 空
  • bit [7:6]: 10 速度100Mhz
  • bit [5:3]: 110 R0/6驅動能力
  • bit [2:1]: 00 空
  • bit [0]: 0 低轉換率
寫下來就是00001 0000 1011 0000,也就是0x10B0,使用程式碼實現,就是
    ldr r0,=0x020e02f4
    ldr r1,=0x10b0
    str r1,[r0]

第四步是將GPIO設定為輸出模式,也就是將GPIO1_GDIR的第2個bit置1,換算值為0x8,GPIO1_GDIR的地址為0x209c004

    ldr r0,=0x0209c004
    ldr r1,=0x8
    str r1,[r0]

下一步就是給GPIO的資料暫存器賦值,注意看前面的硬體原理圖,我們的LED要想點亮的話控制端需要為低電平,所以直接把GPIO1的值置0就好,GPIO1_DR的地址為0x209c000

    ldr r0,=0x0209C000
    ldr r1,=0
    str r1,[r0]

最後放個死迴圈,讓程式跑起來就行了

loop:
    b loop

最後放上所有的程式碼

.global _start @全域性標號
_start:
@ 開啟所有時鐘CCM_CCGR0-6

    /*使能所有外設時鐘 */
    ldr r0,=0x020c4068   @CCGR0
    ldr r1,=0xffffffff   @向CCGR0待寫入的資料
    str r1,[r0]          @將r1裡的資料寫入r0地址的暫存器內

@設定複用IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 5
    ldr r0,=0x020e0068
    ldr r1,=0x5
    str r1,[r0]

@設定電氣屬性IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 地址0x020E02F4
/*
    @  *bit 16:0 HYS關閉
    @  *bit [15:14]: 00 預設下拉
    @  *bit [13]: 0 kepper功能
    @  *bit [12]: 1 pull/keeper使能
    @  *bit [11]: 0 關閉開路輸出
    @  *bit [10-8]: 000 空
    @  *bit [7:6]: 10 速度100Mhz
    @  *bit [5:3]: 110 R0/6驅動能力
    @  *bit [2:1]:  00  空
    @  *bit [0]: 0 低轉換率
    @   00001 0000 1011 0000>0x10B0
 */

    ldr r0,=0x020e02f4
    ldr r1,=0x10b0
    str r1,[r0]

/*設定GPIO1_IO03為輸出,
GPIO1_GDIR地址為 0x209_C004
IO03對應第2bit,值為0x8
*/
    ldr r0,=0x0209C004
    ldr r1,=0x8
    str r1,[r0]

/*設定GPIO輸出高電平
GPIO1_DR,地址為0x209_C000
IO03對應第3bit,低電平時燈點亮,值為0
*/

    ldr r0,=0x0209C000
    ldr r1,=0
    str r1,[r0]

loop:
    b loop
code

整個程式碼的流程就完了,因為程式碼已經跑過,我這裡就不講除錯的過程了,下一章再仔細講一下如何編譯程式碼