IMX6ULL學(xué)習(xí)筆記(20)——UART串口使用

一、UART簡介

i.MX6U 芯片具有多達(dá) 8 個(gè) UART 外設(shè)用于串口通訊,UART 是在 USART 基礎(chǔ)上裁剪掉了同步通信功能娘锁,只支持異步通信。簡單區(qū)分同步和異步就是看通信時(shí)需不需要對外提供時(shí)鐘輸出饺鹃,我們平時(shí)用的串口通信基本都是 UART莫秆。

UART 滿足外部設(shè)備對工業(yè)標(biāo)準(zhǔn) NRZ 異步串行數(shù)據(jù)格式的要求间雀,并且使用了小數(shù)波特率發(fā)生器,可以提供多種波特率镊屎,使得它的應(yīng)用更加廣泛惹挟。UART 支持異步單向通信和半雙工單線通信;還支持局域互連網(wǎng)絡(luò) LIN缝驳、智能卡(SmartCard)協(xié)議與 lrDA(紅外線數(shù)據(jù)協(xié)會)SIR ENDEC 規(guī)范连锯。

i.MX6U 的 UART 主要特性如下:

  • 兼容高速 TIA/EIA-232-F,最高 5.0 Mbit/s用狱。
  • 兼容 IrDA 串行低速紅外接口运怖,最高 115.2 Kbit/s。
  • 支持 9 位或多點(diǎn)(Multidrop mode)模式(RS-485)(自動從地址檢測)夏伊。
  • 支持 7 位或 8 位 RS-232 格式或 9 位的 RS-485 格式摇展。
  • 支持 1 或 2 位停止位。
  • 可編程奇校驗(yàn)或偶校驗(yàn)溺忧。
  • 支持硬件流控(CTS/RTS)咏连。
  • RTS支持邊緣中斷檢測。
  • 自動波特率檢測(最高為115.2kbit/s)鲁森。
  • rx_data 輸入和 tx_data 輸出可分別在 rs-232/rs-485 模式下進(jìn)行交換(軟件可交換 TX祟滴、RX 引腳)。
  • 兩個(gè) DMA 請求(TxFIFODMA 請求和 RxFIFODMA 請求)歌溉。
  • 支持軟件復(fù)位 (Srst_B)踱启。
  • 兩個(gè)獨(dú)立的 32 輸入 FIFO,用于發(fā)送和接收研底。

二埠偿、引腳分布


相比 STM32,i.MX6U 的串口增加了引腳交換功能榜晦。通過配置 UFCR[DCEDTE] 寄存器可以交換 TX冠蒋、RX 引腳以及 CTS、RTS 引腳乾胶,如下圖所示抖剿。


三、波特率設(shè)置

3.1 串口時(shí)鐘

串口模塊共有兩個(gè)時(shí)鐘輸入 Peripheral ClockModule Clock识窿。

  • Peripheral Clock
    外部時(shí)鐘斩郎。這個(gè)時(shí)鐘主要用于讀、寫接收和發(fā)送的數(shù)據(jù)喻频,例如讀接收FIFO缩宜、寫發(fā)送FIFO。 這個(gè)時(shí)鐘與波特率設(shè)置無關(guān),如果沒有特殊需求我們將這個(gè)時(shí)鐘保持默認(rèn)即可锻煌,在初始代碼中并沒有特意設(shè)置這個(gè)時(shí)鐘妓布。

  • Module Clock
    模塊時(shí)鐘,它既可用于接收宋梧、發(fā)送數(shù)據(jù)也用于設(shè)置波特率匣沼,這個(gè)時(shí)鐘決定了串口最高支持的波特率。Module Clock 時(shí)鐘來自根時(shí)鐘 UART_CLK_ROOT捂龄。

從圖中可以看出释涛,從PLL時(shí)鐘到UART時(shí)鐘共用用到了兩個(gè)時(shí)鐘選擇寄存器(標(biāo)號①和③),兩個(gè)時(shí)鐘分頻寄存器(標(biāo)號②和標(biāo)號④)倦沧。 我們最終目的是將PLL3時(shí)鐘作為UART根時(shí)鐘(UART_CLK_ROOT)的根時(shí)鐘枢贿。按照標(biāo)號順序講解如下:

  1. 標(biāo)號①選擇 PLL3 時(shí)鐘還是 CCM_PLL3_BYP。我們選擇 PLL3 輸出時(shí)鐘刀脏,寄存器 CCSR[PLL3_SW_CLK_SEL] = 0局荚, 則表示選擇 PLL3 時(shí)鐘。默認(rèn)情況下是這樣設(shè)置的愈污。所以我們代碼中并沒有設(shè)置該寄存器耀态。
  2. 標(biāo)號②設(shè)置時(shí)鐘分頻,根據(jù)之前的設(shè)置暂雹,PLL3 的輸出頻率為 480MHz 首装,這里的時(shí)鐘分頻是固定的 6 分頻, 經(jīng)過分頻后的時(shí)鐘為 480MHz / 6 = 80MHz杭跪。
  3. 標(biāo)號③ 再次選擇時(shí)鐘源仙逻。一個(gè)是 PLL3 分頻得到的 80MHz 時(shí)鐘,另外一個(gè)是 OSC 時(shí)鐘即 24MHz 的系統(tǒng)參考時(shí)鐘涧尿。 設(shè)置 CSCDR1[UART_CLK_SEL] = 0系奉,選擇第一個(gè)(80MHz)時(shí)鐘。
  4. 標(biāo)號④再次進(jìn)行時(shí)鐘分頻姑廉。這是一個(gè) 6 位的時(shí)鐘分頻寄存器缺亮。分頻值為 CSCDR1[UART_CLK_PODF] 寄存器值加一。 程序中將其設(shè)置為 1桥言,則分頻系數(shù)為 2萌踱,UART_CLK_ROOT 時(shí)鐘頻率實(shí)際為 80MHz / 2 = 40MHz

3.2 波特率計(jì)算公式

UART的發(fā)送器和接收器使用相同的波特率号阿。計(jì)算公式如下:

  • BaudRate:要設(shè)置的波特率并鸵。
  • Ref Freq:參考時(shí)鐘,這個(gè)時(shí)鐘是 Module Clock 經(jīng)過 RFDIV 寄存器分頻后得到的頻率扔涧。
  • UBMR:UBMR寄存器值园担。
  • UBIR:UBIR 寄存器值。

3.3 配置UARTx_UFCR寄存器

假設(shè)目標(biāo)波特率為 115200

3.3.1 設(shè)置時(shí)鐘分頻寄存器RFDIV

通過設(shè)置時(shí)鐘分頻寄存器 RFDIV粉铐,得到參考時(shí)鐘 Ref Freq。

Ref Freq 時(shí)鐘應(yīng)該被設(shè)置為多少卤档,沒有一個(gè)準(zhǔn)確的數(shù)字蝙泼,遵守以下幾條:

  1. Ref Freq 時(shí)鐘要大于波特率的 16 倍

依據(jù)是 module_clock 必須大于等于 16 倍波特率【參考《IMX6ULRM》(參考手冊)53.4.2.1 Clock requirements劝枣,而 module_clock 經(jīng)過 Clock Gating & Divider 之后變?yōu)?ref_clk汤踏,從功能框圖看 ref_clk 最終作為 uart 模塊參考時(shí)鐘】。


  1. UBMR 和 UBIR 的值必須小于 0xFFFF舔腾。

module_clock 時(shí)鐘經(jīng)過 UARTx_UFCR[RFDIV] 寄存器分頻后得到 Ref Freq 時(shí)鐘溪胶,如下圖所示。

在程序中 Module Clock 被設(shè)置為 40MHz 而波特率只有 115200稳诚,所以這里將分頻值設(shè)置為最大值 7哗脖,即UARTx_UFCR[RFDIV] = 110B。得到參考時(shí)鐘 Ref Freq = 40MHz / 7 = 5.71MHz扳还。

3.3.2 計(jì)算UBMR和UBIR的值

已知波特率 115200才避,參考時(shí)鐘 Ref Freq = 5.71MHz,可以計(jì)算得到 (UBMR+1) / (UBIR+1) 約為 3.10氨距。 我們設(shè)置 (UBIR+1) = 10桑逝,(UBMR+1) = 31

四俏让、接收和發(fā)送FIFO

Tx Block 與 Rx Block 包括三部分:

  • 控制單元Control
    控制整個(gè)串口的工作楞遏, 我們編寫軟件不必過多關(guān)心。

  • 電源管理單元 Power Saving

  • TxFIFO (和RxFIFO)
    TxFIFO 與 RxFIFO 大小均為 32 字節(jié)首昔,以發(fā)送為例寡喝,數(shù)據(jù)通過 UTXD 寄存器自動寫入 TxFIFO,如果 TxFIFO沒有滿勒奇,則可以不斷將數(shù)據(jù)寫入 UTXD 寄存器拘荡。UTS[4] 寄存器是 TxFIFO 滿的標(biāo)志位。如果關(guān)閉了發(fā)送撬陵,仍然可以向 TxFIFO 寫入數(shù)據(jù)珊皿,但這樣做將會產(chǎn)生傳輸錯誤信息。當(dāng) TxFIFO 中的數(shù)據(jù)發(fā)送完成后將會自動設(shè)置發(fā)送緩沖區(qū)空中斷標(biāo)志位巨税,向 TxFIFO 寫入數(shù)據(jù)將自動清除發(fā)送緩沖區(qū)空標(biāo)志位蟋定。

五、DMA和中斷請求

每個(gè)串口有兩個(gè) DMA 請求草添,txfifo dma 請求和 rxfifo dma 請求驶兜,有多個(gè)中斷請求。

這里只介紹幾個(gè)常用的中斷:

中斷事件 使能位 標(biāo)志位
空閑中斷 UARTx_UCR1[TRDYEN] UARTx_USR2[TXFE]
接收溢出中斷 UARTx_UCR4[OREN] UARTx_USR2[ORE]
接收緩沖區(qū)非空中斷 UARTx_UCR4[DREN] UARTx_USR2[RDR]

完整內(nèi)容請參考《IMX6ULRM》(參考手冊)53.4.1 Interrupts and DMA Requests。

六抄淑、編程流程

1. 創(chuàng)建工程文件夾
2. 移植官方SDK寄存器定義文件
3. 移植野火PAD屬性配置文件
4. 編寫啟動文件
5. 編寫鏈接文件
6. 編寫makefile文件
7. 編寫C語言代碼
(1) 設(shè)置UART時(shí)鐘源
(2) 初始化UART
(3) 使能UART
(4) UART數(shù)據(jù)接收
(5) UART數(shù)據(jù)發(fā)送

七屠凶、創(chuàng)建工程文件夾

  1. 創(chuàng)建一個(gè)文件夾 uart
  2. 創(chuàng)建一個(gè)用于存放頭文件的文件夾 include
  3. 創(chuàng)建一個(gè)用于存放驅(qū)動源碼的文件 device
  4. 創(chuàng)建一個(gè)啟動文件 start.S
  5. 創(chuàng)建一個(gè)源文件 main.c
  6. 創(chuàng)建一個(gè)鏈接腳本 base.lds

八、移植官方SDK寄存器定義文件

/uart/include 目錄下添加官方SDK寄存器定義文件 MCIMX6Y2.h肆资,位于 SDK_2.2_MCIM6ULL_EBF6ULL/devices/MCIMX6Y2 目錄下矗愧。

在官方SDK的頭文件 MCIMX6Y2.h 文件多達(dá)4萬多行,包含了i.MX6U芯片幾乎所有的寄存器定義以及中斷編號的定義郑原。

這里只列 GPIO1相關(guān)寄存器 的部分代碼唉韭。其他寄存器定義與此類似。 添加這些定義之后我們就可以 直接使用 “GPIO1->DR” 語句操作GPIO1的DR寄存器犯犁。操作方法與STM32非常相似属愤。

typedef struct {
   __IO uint32_t DR;     /**< GPIO data register, offset: 0x0 */
   __IO uint32_t GDIR;   /**< GPIO direction register, offset: 0x4 */
   __I  uint32_t PSR;    /**< GPIO pad status register, offset: 0x8 */
   __IO uint32_t ICR1;   /**< GPIO interrupt configuration register1,*/
   __IO uint32_t ICR2;   /**< GPIO interrupt configuration register2, */
   __IO uint32_t IMR;   /**< GPIO interrupt mask register, offset: 0x14 */
   __IO uint32_t ISR; /**< GPIO interrupt status register, offset: 0x18 */
   __IO uint32_t EDGE_SEL;/**< GPIO edge select register, offset: 0x1C */
} GPIO_Type;

/*********************以下代碼省略***************************8*/
/** Peripheral GPIO1 base address */
#define GPIO1_BASE                               (0x209C000u)
/** Peripheral GPIO1 base pointer */
#define GPIO1                                    ((GPIO_Type *)GPIO1_BASE)

九、移植野火PAD屬性配置文件

/uart/device 目錄下添加 pad_config.h酸役。

通常情況下一個(gè)引腳要設(shè)置8種PAD屬性住诸,而這些屬性只能通過數(shù)字指定。為簡化PAD屬性設(shè)置野火編寫了一個(gè)PAD屬性配置文件 pad_config.h (embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c/pad_config.h)【源碼下載:https://gitee.com/Embedfire/embed_linux_driver_tutorial_imx6_code.git】涣澡,這里使用宏定義了引腳可選的PAD屬性值只壳,并且通過宏定義的名字很容易知道宏代表的屬性值:

/* SPEED 帶寬配置 */
#define SPEED_0_LOW_50MHz       IOMUXC_SW_PAD_CTL_PAD_SPEED(0)
#define SPEED_1_MEDIUM_100MHz   IOMUXC_SW_PAD_CTL_PAD_SPEED(1)
#define SPEED_2_MEDIUM_100MHz   IOMUXC_SW_PAD_CTL_PAD_SPEED(2)
#define SPEED_3_MAX_200MHz      IOMUXC_SW_PAD_CTL_PAD_SPEED(3)


/* PUE 選擇使用保持器還是上下拉 */
#define PUE_0_KEEPER_SELECTED       IOMUXC_SW_PAD_CTL_PAD_PUE(0)
#define PUE_1_PULL_SELECTED         IOMUXC_SW_PAD_CTL_PAD_PUE(1)


/* PUS 上下拉配置 */
#define PUS_0_100K_OHM_PULL_DOWN  IOMUXC_SW_PAD_CTL_PAD_PUS(0)
#define PUS_1_47K_OHM_PULL_UP     IOMUXC_SW_PAD_CTL_PAD_PUS(1)
#define PUS_2_100K_OHM_PULL_UP    IOMUXC_SW_PAD_CTL_PAD_PUS(2)
#define PUS_3_22K_OHM_PULL_UP     IOMUXC_SW_PAD_CTL_PAD_PUS(3)

完整的代碼請閱讀源文件,這里只列出了文件“pad_config.h”部分代碼(embed_linux_driver_tutorial_imx6_code/bare_metal/led_rgb_c/pad_config.h)【源碼下載:https://gitee.com/Embedfire/embed_linux_driver_tutorial_imx6_code.git】暑塑。

十吼句、編寫啟動文件

/uart 下創(chuàng)建 start.S 文件用于編寫啟動文件。
在匯編文件中設(shè)置“棧地址”并執(zhí)行跳轉(zhuǎn)命令跳轉(zhuǎn)到main函數(shù)執(zhí)行C代碼事格。

10.1 完整代碼

/***********************第一部分*********************/
  .text            //代碼段
  .align 2         //設(shè)置2字節(jié)對齊
  .global _start   //定義一個(gè)全局標(biāo)號

/*************************第二部分*************************/
  _start:          //程序的開始
    b reset      //跳轉(zhuǎn)到reset標(biāo)號處

/*************************第三部分*************************/
reset:
   mrc     p15, 0, r0, c1, c0, 0     /*  將 CP15 協(xié)處理器中的寄存器數(shù)據(jù)讀到 ARM 寄存器中   */
   bic     r0,  r0, #(0x1 << 12)     /*  清除第12位(I位)禁用 I Cache  */
   bic     r0,  r0, #(0x1 <<  2)     /*  清除第 2位(C位)禁用 D Cache  */
   bic     r0,  r0, #0x2             /*  清除第 1位(A位)禁止嚴(yán)格對齊   */
   bic     r0,  r0, #(0x1 << 11)     /*  清除第11位(Z位)分支預(yù)測   */
   bic     r0,  r0, #0x1             /*  清除第 0位(M位)禁用 MMU   */
   mcr     p15, 0, r0, c1, c0, 0     /*  將 ARM 寄存器的數(shù)據(jù)寫入到 CP15 協(xié)處理器寄存器中   */

/***********************第四部分*********************/
      ldr sp, =0x84000000   //設(shè)置棧地址64M
      b main                //跳轉(zhuǎn)到main函數(shù)

/***********************第五部分*******************/
    /*進(jìn)入死循環(huán)*/
  loop:
      b loop

10.2 分析代碼

  • 第一部分
    .text 定義代碼段惕艳。
    .align 2 設(shè)置字節(jié)對齊。
    .global _start 生命全局標(biāo)號_start驹愚。
/*************************第一部分*************************/
.text            //代碼段
.align 2         //設(shè)置2字節(jié)對齊
.global _start   //定義一個(gè)全局標(biāo)號
  • 第二部分
    _start: 定義標(biāo)號_start: 远搪,它位于匯編的最前面,說以會首先被執(zhí)行逢捺。
    b reset 使用b指令將程序跳轉(zhuǎn)到reset標(biāo)號處谁鳍。
/*************************第二部分*************************/
_start:          //程序的開始
   b reset      //跳轉(zhuǎn)到reset標(biāo)號處
  • 第三部分
    通過修改CP15寄存器(系統(tǒng)控制寄存器) 關(guān)閉 I Cache 、D Cache劫瞳、MMU 等等倘潜。
    我們暫時(shí)用不到的功能,如果開啟可能會影響我們裸機(jī)運(yùn)行志于,為避免不必要的麻煩暫時(shí)關(guān)閉這些功能涮因。
/*************************第三部分*************************/
reset:
   mrc     p15, 0, r0, c1, c0, 0     /*  將 CP15 協(xié)處理器中的寄存器數(shù)據(jù)讀到 ARM 寄存器中   */
   bic     r0,  r0, #(0x1 << 12)     /*  清除第12位(I位)禁用 I Cache  */
   bic     r0,  r0, #(0x1 <<  2)     /*  清除第 2位(C位)禁用 D Cache  */
   bic     r0,  r0, #0x2             /*  清除第 1位(A位)禁止嚴(yán)格對齊   */
   bic     r0,  r0, #(0x1 << 11)     /*  清除第11位(Z位)分支預(yù)測   */
   bic     r0,  r0, #0x1             /*  清除第 0位(M位)禁用 MMU   */
   mcr     p15, 0, r0, c1, c0, 0     /*  將 ARM 寄存器的數(shù)據(jù)寫入到 CP15 協(xié)處理器寄存器中   */
  • 第四部分
    ldr sp, =0x84000000 用于設(shè)置棧指針。野火i.MX6ULL開發(fā)板標(biāo)配512M的DDR內(nèi)存伺绽,裸機(jī)開發(fā)用不了這么多养泡。程序中我們將棧地址設(shè)置到DDR的64M地址處嗜湃。 這個(gè)值也可以根據(jù)需要自行定義。
    b main 只用跳轉(zhuǎn)指令跳轉(zhuǎn)到main函數(shù)中執(zhí)行澜掩。
/***********************第四部分*********************/
      ldr sp, =0x84000000   //設(shè)置棧地址64M
      b main                //跳轉(zhuǎn)到main函數(shù)
  • 第五部分
    b loop 是“無返回”的跳轉(zhuǎn)指令购披。正常情況下,不會執(zhí)行第五部分代碼肩榕。
/***********************第五部分*******************/
  /*進(jìn)入死循環(huán)*/
  loop:
      b loop

十一刚陡、編寫鏈接腳本

寫好的代碼(無論是匯編還是C語言)都要經(jīng)過編譯、匯編点把、鏈接等步驟生成二進(jìn)制文件或者可供下載的文件橘荠。在編譯階編譯器會對每個(gè)源文件進(jìn)行語法檢查并生成對應(yīng)的匯編語言屿附,匯編是將匯編文件轉(zhuǎn)化為機(jī)器碼郎逃。

使用 arm-none-eabi-gcc -g -c led.S -o led.o 命令完成源碼的編譯、匯編工作挺份,生成了 .o文件褒翰。編譯和匯編是針對單個(gè)源文件,也就編譯完成后一個(gè)源文件(.c匀泊,.S.s)對應(yīng)一個(gè) .o 文件优训。程序鏈接階段就會將這些 .o 鏈接成一個(gè)文件。

鏈接腳本的作用就是告訴編譯器怎么鏈接這些文件各聘,比如那個(gè)文件放在最前面揣非,程序的代碼段、數(shù)據(jù)段躲因、bss段分別放在什么位置等等早敬。

/uart 下創(chuàng)建 base.lds 鏈接腳本。

11.1 完整代碼

 ENTRY(_start)
 SECTIONS {
   . = 0x80000000;

   . = ALIGN(4);
   .text :
   {
   start.o (.text)
   *(.text)
   }

   . = ALIGN(4);
   .data :
   {
   *(.data)
   }

   . = ALIGN(4);
   .bss :
   {
   *(.bss)
   }
 }

11.2 分析代碼

  • 指定程序的入口
    ENTRY(_start) 用于指定程序的入口大脉,ENTRY() 是設(shè)置入口地址的命令搞监, “_start” 是程序的入口,led程序的入口地址位于 start.S“_start” 標(biāo)號處镰矿。
 ENTRY(_start)
  • 定義SECTIONS
    SECTIONS 可以理解為是一塊區(qū)域琐驴,我們在這塊區(qū)域排布我們的代碼,鏈接時(shí)鏈接器就會按照這里的指示鏈接我們的代碼秤标。
 SECTIONS {
···
···
}
  • 定義鏈接起始地址
    “.” 運(yùn)算符代表當(dāng)前位置绝淡。 我們在SECTION的最開始使用 “.= 0x80000000” 就是將鏈接起始地址設(shè)置為0x80000000。
. = 0x80000000;
  • 設(shè)置字節(jié)對齊
    “. = ALIGN(4);” 它表示從當(dāng)前位置開始執(zhí)行四字節(jié)對齊苍姜。假設(shè)當(dāng)前位置為0x80000001够委,執(zhí)行該命令后當(dāng)前地址將會空出三個(gè)字節(jié)轉(zhuǎn)到0x80000004地址處。

  • 設(shè)置代碼段
    “.text :” 用于定義代碼段怖现,固定的語法要求茁帽,我們按照要求寫即可玉罐。在“{}”中指定那些內(nèi)容放在代碼段。
    start.o 中的代碼放到代碼段的最前面潘拨。start.S是啟動代碼應(yīng)當(dāng)首先被執(zhí)行吊输,所以通常情況下要把它放到代碼段的最前面,其他源文件的代碼按照系統(tǒng)默認(rèn)的排放順序即可铁追,通配符 “*” 在這里表示其他剩余所有的 .o文件季蚂。

   . = ALIGN(4);
   .text :
   {
   start.o (.text)
   *(.text)
   }
  • 設(shè)置數(shù)據(jù)段
    同設(shè)置代碼段類似,首先設(shè)置字節(jié)對齊琅束,然后定義代碼段粘招。在數(shù)據(jù)段里使用 “*” 通配符, 將所有源文件中的代碼添加到這個(gè)數(shù)據(jù)段中削葱。
   . = ALIGN(4);
   .data :
   {
   *(.data)
   }
  • 設(shè)置BSS段
    設(shè)置方法與設(shè)置數(shù)據(jù)段完全相同咆繁。
. = ALIGN(4);
   .bss :
   {
   *(.bss)
   }

十二、編寫makefile文件

程序編寫完成后需要依次輸入編譯艾船、鏈接葵腹、格式轉(zhuǎn)換命令才能最終生成二進(jìn)制文件。這種編譯方式效率低屿岂、容易出錯践宴。

使用makefile只需要在所在文件夾下執(zhí)行make命令,makefile工具便會自動完成程序的編譯爷怀、鏈接阻肩、格式轉(zhuǎn)換等工作。正常情況下我們可以在當(dāng)前目錄看到生成的一些中間文件以及我們期待的.bin文件运授。

修改makefile主要包括兩部分

  • 第一部分烤惊,在“device”文件夾下添加并編寫子makefile。
  • 第二部分徒坡,修改主makefile撕氧。

12.1 編寫子makefile

/uart/device 下創(chuàng)建 makefile

子makefile: 用于將“device”文件夾下的驅(qū)動源文件編譯為一個(gè)“.o”文件

all : led.o system_MCIMX6Y2.o uart.o
    arm-none-eabi-ld -r $^  -o device.o
    
%.o : %.c
    arm-none-eabi-gcc ${header_file} -c $^
    
%.o : %.S
    arm-none-eabi-gcc ${header_file} -c $^

clean:
    -rm -f *.o *.bak
  • 添加最終目標(biāo)以及依賴文件
    生成最終目標(biāo)“device.o”喇完。如果程序中新增了某個(gè)外設(shè)驅(qū)動程序伦泥,只需要將對應(yīng)的“.o”文件填入“依賴”處即可。
    “$^” 代表所有的依賴文件锦溪。
    “-o” 指定輸出文件名不脯。
all : button.o  led.o system_MCIMX6Y2.o
    arm-none-eabi-ld -r $^  -o device.o
  • 添加編譯C文件的命令
    編譯“device”文件夾下的所有“.c”文件并生成對應(yīng)的“.o”文件,其中“header_file”是頭文件路徑刻诊,它是定義在主makefile的變量防楷。
    “$^” 替代要編譯的源文件。
%.o : %.c
  arm-none-eabi-gcc ${header_file} -c $^
  • 添加匯編文件編譯命令
    編譯“device”文件夾下的所有“.S”文件并生成對應(yīng)的“.o”文件则涯,其中“header_file”是頭文件路徑复局,它是定義在主makefile的變量冲簿。
    “$^” 替代要編譯的源文件。
%.o : %.S
  arm-none-eabi-gcc ${header_file} -c $^
  • 添加清理命令
    “clean” 為目標(biāo)用于刪除make生成的文件亿昏。
clean:
  -rm -f *.o *.bak

12.2 修改主makefile

主makefile的改動主要有兩點(diǎn):

  1. 在編譯命令中指明頭文件位置峦剔。
  2. 使用命令調(diào)用子makefile,生成依賴文件角钩。
#定義變量吝沫,用于保存編譯選項(xiàng)和頭文件保存路徑
header_file := -fno-builtin -I$(shell pwd)/include
export header_file

/*arm-none-eabi 安裝位置*/
libgcc_address := /usr/lib/gcc/arm-none-eabi/6.3.1

all : start.o main.o device/device.o 
    arm-none-eabi-ld -Tbase.lds $^ -o base.elf -static -L $(libgcc_address) -lgcc
    arm-none-eabi-objcopy -O binary -S -g base.elf base.bin


%.o : %.S
    arm-none-eabi-gcc -g -c $^ 
%.o : %.c
    arm-none-eabi-gcc $(header_file) -c $^  

#調(diào)用其他文件的makefile
device/device.o :
    make -C device all


.PHONY: copy
copy:
    cp ./base.bin  /home/pan/download/embedfire

#定義清理偽目標(biāo)
.PHONY: clean
clean:
    make -C device clean
    -rm -f *.o *.elf *.bin 
  • 添加編譯選項(xiàng)和頭文件保存路徑
    定義變量 “header_file”。在makefile中“變量”更像C原因中的宏定義递礼。
    “-fno-builtin” 是一個(gè)編譯選項(xiàng)惨险,用于解決庫函數(shù)與自己編寫函數(shù)同名問題。
    “-I$(shell pwd)/include” 用于指定頭文件路徑脊髓。
    “export header_file” 聲明后可以在其他makefile中調(diào)用辫愉。
header_file := -fno-builtin -I$(shell pwd)/include
export header_file
  • 添加鏈接庫路徑
/*arm-none-eabi 安裝位置*/
libgcc_address := /usr/lib/gcc/arm-none-eabi/6.3.1

“l(fā)ibgcc_address” 用于保存”arm-none-eabi”編譯工具的安裝位置,我們需要的libgcc.a位于該目錄下供炼。如果使用”sudo apt-get install gcc-arm-none-eabi”安裝則默認(rèn)位于程序中所指目錄一屋,如果使用其他方式安裝窘疮,找到對應(yīng)的路徑填入該變量即可袋哼。

  • 添加最終目標(biāo)以及依賴文件
all : start.o main.o device/device.o
  • 添加鏈接命令
    “-Tbase.lds” 表示使用base.lds鏈接腳本鏈接程序。
    “$^” 代表所有的依賴文件闸衫。
    “-o” 指定輸出文件名涛贯。
    “-static -L $(libgcc_address) -lgcc” 添加靜態(tài)鏈接libgcc.a庫文件。
arm-none-eabi-ld -Tbase.lds $^ -o base.elf -static -L $(libgcc_address) -lgcc
  • 添加格式轉(zhuǎn)換命令
    “-O binary” 指定輸出二進(jìn)制文件蔚出。
    “-S” 不從源文件中復(fù)制重定位信息和符號信息弟翘。
    “-g” 不從源文件中復(fù)制可調(diào)試信息。
arm-none-eabi-objcopy -O binary -S -g base.elf base.bin
  • 添加匯編文件編譯命令
    “$^” 替代要編譯的源文件骄酗。
%.o : %.S
  arm-none-eabi-gcc -g -c $^
  • 添加編譯C文件的命令
    “$^” 替代要編譯的源文件稀余。
%.o : %.c
  arm-none-eabi-gcc $(header_file) -c $^
  • 添加調(diào)用其他文件的makefile
    定義生成“device/device.o”的命令,“device.o”文件由子makefile生成趋翻,所以這里只需要調(diào)用子makefile即可睛琳。
device/device.o :
  make -C device all
  • 添加清理命令
    在清理命令中不但要清理主makefile所在文件夾的內(nèi)容還要調(diào)用子makefile的清理命令以清理子makefile所在文件夾的內(nèi)容。
    “.PHONY” 定義了偽目標(biāo)“clean”踏烙。偽目標(biāo)一般沒有依賴师骗,并且 “clean” 偽目標(biāo)一般放在Makefile文件的末尾。
    “clean” 為目標(biāo)用于刪除make生成的文件讨惩。
.PHONY: clean
clean:
  make -C device clean
  -rm -f *.o *.elf *.bin

十三辟癌、編寫C語言代碼

13.1 添加串口初始化和接收發(fā)送代碼

13.1.1 uart.h

/uart/include 下創(chuàng)建 uart.h

#ifndef uart_h
#define uart_h


#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "pad_config.h"

#define  uint32_t  unsigned int
#define  uint64_t  unsigned long int



/*定義 UART1 RX 引腳*/
#define UART1_RX_GPIO                GPIO1
#define UART1_RX_GPIO_PIN            (17U)
#define UART1_RX_IOMUXC              IOMUXC_UART1_RX_DATA_UART1_RX

/*定義 UART1 TX 引腳*/
#define UART1_TX_GPIO              GPIO1
#define UART1_TX_GPIO_PIN          (16U)
#define UART1_TX_IOMUXC            IOMUXC_UART1_TX_DATA_UART1_TX


/*******************************************************************************
 * uart引腳配置
 ******************************************************************************/
#define UART_RX_PAD_CONFIG_DATA            (SRE_0_SLOW_SLEW_RATE| \
                                        DSE_6_R0_6| \
                                        SPEED_1_MEDIUM_100MHz| \
                                        ODE_0_OPEN_DRAIN_DISABLED| \
                                        PKE_1_PULL_KEEPER_ENABLED| \
                                        PUE_1_PULL_SELECTED| \
                                        PUS_3_22K_OHM_PULL_UP| \
                                        HYS_0_HYSTERESIS_DISABLED) 
    /* 配置說明 : */
    /* 轉(zhuǎn)換速率: 轉(zhuǎn)換速率慢
        驅(qū)動強(qiáng)度: R0/6 
        帶寬配置 : medium(100MHz)
        開漏配置: 關(guān)閉 
        拉/保持器配置: 使能
        拉/保持器選擇: 上下拉
        上拉/下拉選擇: 22K歐姆上拉(選擇了保持器此配置無效)
        滯回器配置: 禁止 */ 

#define UART_TX_PAD_CONFIG_DATA            (SRE_0_SLOW_SLEW_RATE| \
                                        DSE_6_R0_6| \
                                        SPEED_1_MEDIUM_100MHz| \
                                        ODE_0_OPEN_DRAIN_DISABLED| \
                                        PKE_1_PULL_KEEPER_ENABLED| \
                                        PUE_0_KEEPER_SELECTED| \
                                        PUS_3_22K_OHM_PULL_UP| \
                                        HYS_0_HYSTERESIS_DISABLED)
    /* 配置說明 : */
    /* 轉(zhuǎn)換速率: 轉(zhuǎn)換速率慢
        驅(qū)動強(qiáng)度: R0/6 
        帶寬配置 : medium(100MHz)
        開漏配置: 關(guān)閉 
        拉/保持器配置: 使能
        拉/保持器選擇: 保持器
        上拉/下拉選擇: 22K歐姆上拉(選擇了保持器此配置無效)
        滯回器配置: 禁止 */ 



void uart_init(void);
int32_t UART_SetBaudRate(UART_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz);

void UART_WriteBlocking(UART_Type *base, const uint8_t *data, uint8_t length);
void UART_ReadBlocking(UART_Type *base, uint8_t *data, uint8_t length);

#endif

13.1.2 uart.c

/uart/device 下創(chuàng)建 uart.c荐捻。

#include "uart.h"

void uart_init(void)
{
    /*時(shí)鐘初始化黍少,設(shè)置 UART 根時(shí)鐘寡夹,并設(shè)置為40MHz*/
    CCM->CSCDR1 &= ~(0x01 << 6); //設(shè)置UART選擇 PLL3 / 6 = 80MHz
    CCM->CSCDR1 &= ~(0x3F);      //清零
    CCM->CSCDR1 |= (0x01 << 0);  //設(shè)置串口根時(shí)鐘分頻值為1,UART根時(shí)鐘頻率為:80M / (dev + 1) = 40MHz

    /*開啟 UART1 的時(shí)鐘*/
    CCM_CCGR5_CG12(0x3); //開啟UART1的時(shí)鐘

    UART1->UCR1 &= ~UART_UCR1_UARTEN_MASK; //禁用 UART1

    /*軟件復(fù)位*/
    UART1->UCR2 &= ~UART_UCR2_SRST_MASK;
    while ((UART1->UCR2 & UART_UCR2_SRST_MASK) == 0)
    {
    }

    UART1->UCR1 = 0x0;
    UART1->UCR2 = UART_UCR2_SRST_MASK;
    UART1->UCR3 = UART_UCR3_DSR_MASK | UART_UCR3_DCD_MASK | UART_UCR3_RI_MASK;
    UART1->UCR4 = UART_UCR4_CTSTL(32);
    UART1->UFCR = UART_UFCR_TXTL(2) | UART_UFCR_RXTL(1);
    UART1->UESC = UART_UESC_ESC_CHAR(0x2B);
    UART1->UTIM = 0x0;
    UART1->ONEMS = 0x0;
    UART1->UTS = UART_UTS_TXEMPTY_MASK | UART_UTS_RXEMPTY_MASK;
    UART1->UMCR = 0x0;

    /*引腳初始化*/
    IOMUXC_SetPinMux(UART1_RX_IOMUXC, 0);
    IOMUXC_SetPinConfig(UART1_RX_IOMUXC, UART_RX_PAD_CONFIG_DATA);

    IOMUXC_SetPinMux(UART1_TX_IOMUXC, 0);
    IOMUXC_SetPinConfig(UART1_TX_IOMUXC, UART_TX_PAD_CONFIG_DATA);

    /*******uart初始化******/
    /*設(shè)置控制寄存器到默認(rèn)值*/
    UART1->UCR2 |= (1 << 5);  //8位數(shù)寬度
    UART1->UCR2 &= ~(1 << 6); //一位停止位
    UART1->UCR2 &= ~(1 << 8); //禁用奇偶校驗(yàn)位

    UART1->UCR2 |= (1 << 2);  //使能發(fā)送
    UART1->UCR2 |= (1 << 1);  //使能接收
    UART1->UCR2 |= (1 << 14); //忽略流控

    /* For imx family device, UARTs are used in  mode, so that this bit should always be set.*/
    UART1->UCR3 |= UART_UCR3_RXDMUXSEL_MASK;

    //只有FIFO的數(shù)據(jù)超過閾值才會產(chǎn)生相應(yīng)的中斷厂置,由于沒有使用中斷要出,所以這里將閾值設(shè)置為1即可。
    UART1->UFCR = (UART1->UFCR & ~UART_UFCR_TXTL_MASK) | UART_UFCR_TXTL(1); //設(shè)置發(fā)送FIFO 閥值
    UART1->UFCR = (UART1->UFCR & ~UART_UFCR_TXTL_MASK) | UART_UFCR_TXTL(1); //設(shè)置接收FIFO 閥值

    UART1->UCR1 &= ~UART_UCR1_ADBR_MASK; //禁用可變波特率
    // UART1->UCR1 |= UART_UCR1_ADBR_MASK;

    /*波特率設(shè)置方式 1 农渊。 使用官方SDK設(shè)置波特率函數(shù)*/
    UART_SetBaudRate(UART1, 115200, 40000000);

    /*波特率設(shè)置方式 2 患蹂。 手動計(jì)算,填入寄存器*/
    /*設(shè)置串口波特率
    * Ref Freq時(shí)鐘 40MHz
    * UFCR RFDIV   110  0x06 7分頻    5.714MHz
    * BaudRate     115200bps
    * UBMR         31-1 = 0x09
    * UBIR         10-1 = 0x1E
    */
    UART1->UFCR &= ~(0x07 << 7); //清零分頻值
    UART1->UFCR |= (0x06 << 7);  //設(shè)置分頻值砸紊,40MHz /7 =  5.714MHz

    UART1->UBIR = 0x09;
    UART1->UBMR = 0x1E;

    /*開啟串口*/
    UART1->UCR1 |= UART_UCR1_UARTEN_MASK;
}


/*!
 * 功能:官方SDK 串口字符串讀取函數(shù)
 * @brief Reads the receiver register.
 *
 * This function is used to read data from receiver register.
 * The upper layer must ensure that the receiver register is full or that
 * the RX FIFO has data before calling this function.
 *
 * @param base UART peripheral base address.
 * @return Data read from data register.
 */
static inline uint8_t UART_ReadByte(UART_Type *base)
{
    return (uint8_t)((base->URXD & UART_URXD_RX_DATA_MASK) >> UART_URXD_RX_DATA_SHIFT);
}


/*函數(shù)功能:串口接收函數(shù)
 *參數(shù): base,指定串口传于。data,保存接收到的數(shù)據(jù)。 length醉顽,要接收的數(shù)據(jù)長度
 *
*/
void UART_ReadBlocking(UART_Type *base, uint8_t *data, uint8_t length)
{
    while (length--)
    {
        /* 等待接收完成 */
        while (!(base->USR2 & UART_USR2_RDR_MASK))
        {
        }
        /*讀取接收到的數(shù)據(jù) */
        *(data++) = UART_ReadByte(base);
    }
}


/*!
 * 功能:官方SDK 串口發(fā)送函數(shù)
 * 參數(shù):base沼溜,指定串口。data游添,指定要發(fā)送的字節(jié)
 * This function is used to write data to transmitter register.
 * The upper layer must ensure that the TX register is empty or that
 * the TX FIFO has room before calling this function.
 */
static inline void UART_WriteByte(UART_Type *base, uint8_t data)
{
    base->UTXD = data & UART_UTXD_TX_DATA_MASK;
}

/*
 *功能:官方SDK 串口字符串發(fā)送函數(shù)
 *參數(shù)說明:
*/
void UART_WriteBlocking(UART_Type *base, const uint8_t *data, uint8_t length)
{

    while (length--)
    {
        /* Wait for TX fifo valid.
         * This API can only ensure that the data is written into the data buffer but can't
         * ensure all data in the data buffer are sent into the transmit shift buffer.
         */
        while (!(base->USR2 & UART_USR2_TXDC_MASK))
        {
        }
        UART_WriteByte(base, *(data++));
    }
}

/* 官方SDK 波特率設(shè)置函數(shù)系草,
 * 修改內(nèi)容:修改了函數(shù)的返回值,波特率設(shè)置成功唆涝,返回1 找都。波特率設(shè)置失敗返回 0
 *This UART instantiation uses a slightly different baud rate calculation.
 * Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).
 * To get a baud rate, three register need to be writen, UFCR,UBMR and UBIR
 * At first, find the approximately maximum divisor of src_Clock and baudRate_Bps.
 * If the numerator and denominator are larger then register maximum value(0xFFFF),
 * both of numerator and denominator will be divided by the same value, which
 * will ensure numerator and denominator range from 0~maximum value(0xFFFF).
 * Then calculate UFCR and UBIR value from numerator, and get UBMR value from denominator.
 */
int32_t UART_SetBaudRate(UART_Type *base, uint32_t baudRate_Bps, uint32_t srcClock_Hz)
{
    uint32_t numerator = 0u;
    uint32_t denominator = 0U;
    uint32_t divisor = 0U;
    uint32_t refFreqDiv = 0U;
    uint32_t divider = 1U;
    uint64_t baudDiff = 0U;
    uint64_t tempNumerator = 0U;
    uint32_t tempDenominator = 0u;

    /* get the approximately maximum divisor */
    numerator = srcClock_Hz;
    denominator = baudRate_Bps << 4;
    divisor = 1;

    while (denominator != 0)
    {
        divisor = denominator;
        denominator = numerator % denominator;
        numerator = divisor;
    }

    numerator = srcClock_Hz / divisor;
    denominator = (baudRate_Bps << 4) / divisor;

    /* numerator ranges from 1 ~ 7 * 64k */
    /* denominator ranges from 1 ~ 64k */
    if ((numerator > (UART_UBIR_INC_MASK * 7)) || (denominator > UART_UBIR_INC_MASK))
    {
        uint32_t m = (numerator - 1) / (UART_UBIR_INC_MASK * 7) + 1;
        uint32_t n = (denominator - 1) / UART_UBIR_INC_MASK + 1;
        uint32_t max = m > n ? m : n;
        numerator /= max;
        denominator /= max;
        if (0 == numerator)
        {
            numerator = 1;
        }
        if (0 == denominator)
        {
            denominator = 1;
        }
    }
    divider = (numerator - 1) / UART_UBIR_INC_MASK + 1;

    switch (divider)
    {
        case 1:
            refFreqDiv = 0x05;
            break;
        case 2:
            refFreqDiv = 0x04;
            break;
        case 3:
            refFreqDiv = 0x03;
            break;
        case 4:
            refFreqDiv = 0x02;
            break;
        case 5:
            refFreqDiv = 0x01;
            break;
        case 6:
            refFreqDiv = 0x00;
            break;
        case 7:
            refFreqDiv = 0x06;
            break;
        default:
            refFreqDiv = 0x05;
            break;
    }
    /* Compare the difference between baudRate_Bps and calculated baud rate.
     * Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).
     * baudDiff = (srcClock_Hz/divider)/( 16 * ((numerator / divider)/ denominator).
     */
    tempNumerator = srcClock_Hz;
    tempDenominator = (numerator << 4);
    divisor = 1;
    /* get the approximately maximum divisor */
    while (tempDenominator != 0)
    {
        divisor = tempDenominator;
        tempDenominator = tempNumerator % tempDenominator;
        tempNumerator = divisor;
    }
    tempNumerator = srcClock_Hz / divisor;
    tempDenominator = (numerator << 4) / divisor;
    baudDiff = (tempNumerator * denominator) / tempDenominator;
    baudDiff = (baudDiff >= baudRate_Bps) ? (baudDiff - baudRate_Bps) : (baudRate_Bps - baudDiff);

    if (baudDiff < (baudRate_Bps / 100) * 3)
    {
        base->UFCR &= ~UART_UFCR_RFDIV_MASK;
        base->UFCR |= UART_UFCR_RFDIV(refFreqDiv);
        base->UBIR = UART_UBIR_INC(denominator - 1);
        base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
        base->ONEMS = UART_ONEMS_ONEMS(srcClock_Hz / (1000 * divider));

        return 1;
    }
    else
    {
        return 0;
    }
}
  • 第一部分:設(shè)置 UART 時(shí)鐘源
    設(shè)置 UART 的時(shí)鐘源為 pll3_80m,設(shè)置寄存器 CCM_CSCDR1 的 UART_CLK_SEL 位為 0 即可廊酣。
/*時(shí)鐘初始化能耻,設(shè)置 UART 根時(shí)鐘,并設(shè)置為40MHz*/
CCM->CSCDR1 &= ~(0x01 << 6); //設(shè)置UART選擇 PLL3 / 6 = 80MHz
CCM->CSCDR1 &= ~(0x3F);      //清零
CCM->CSCDR1 |= (0x01 << 0);  //設(shè)置串口根時(shí)鐘分頻值為1亡驰,UART根時(shí)鐘頻率為:80M / (dev + 1) = 40MHz

/*開啟 UART1 的時(shí)鐘*/
CCM_CCGR5_CG12(0x3); //開啟UART1的時(shí)鐘

詳細(xì)查看 IMX6ULL學(xué)習(xí)筆記(19)——時(shí)鐘系統(tǒng)

  • 第二部分:初始化 UART
    初始化 UART 所使用 IO晓猛,設(shè)置 UART1 的寄存器 UARTx_UCR1~UARTx_UCR3,設(shè)置內(nèi)容包括波特率凡辱,奇偶校驗(yàn)戒职、停止位、數(shù)據(jù)位等等透乾。
UART1->UCR1 &= ~UART_UCR1_UARTEN_MASK; //禁用 UART1

/*軟件復(fù)位*/
UART1->UCR2 &= ~UART_UCR2_SRST_MASK;
while ((UART1->UCR2 & UART_UCR2_SRST_MASK) == 0)
{
}

UART1->UCR1 = 0x0;
UART1->UCR2 = UART_UCR2_SRST_MASK;
UART1->UCR3 = UART_UCR3_DSR_MASK | UART_UCR3_DCD_MASK | UART_UCR3_RI_MASK;
UART1->UCR4 = UART_UCR4_CTSTL(32);
UART1->UFCR = UART_UFCR_TXTL(2) | UART_UFCR_RXTL(1);
UART1->UESC = UART_UESC_ESC_CHAR(0x2B);
UART1->UTIM = 0x0;
UART1->ONEMS = 0x0;
UART1->UTS = UART_UTS_TXEMPTY_MASK | UART_UTS_RXEMPTY_MASK;
UART1->UMCR = 0x0;

/*引腳初始化*/
IOMUXC_SetPinMux(UART1_RX_IOMUXC, 0);
IOMUXC_SetPinConfig(UART1_RX_IOMUXC, UART_RX_PAD_CONFIG_DATA);

IOMUXC_SetPinMux(UART1_TX_IOMUXC, 0);
IOMUXC_SetPinConfig(UART1_TX_IOMUXC, UART_TX_PAD_CONFIG_DATA);

/*******uart初始化******/
/*設(shè)置控制寄存器到默認(rèn)值*/
UART1->UCR2 |= (1 << 5);  //8位數(shù)寬度
UART1->UCR2 &= ~(1 << 6); //一位停止位
UART1->UCR2 &= ~(1 << 8); //禁用奇偶校驗(yàn)位

UART1->UCR2 |= (1 << 2);  //使能發(fā)送
UART1->UCR2 |= (1 << 1);  //使能接收
UART1->UCR2 |= (1 << 14); //忽略流控

/* For imx family device, UARTs are used in  mode, so that this bit should always be set.*/
UART1->UCR3 |= UART_UCR3_RXDMUXSEL_MASK;

//只有FIFO的數(shù)據(jù)超過閾值才會產(chǎn)生相應(yīng)的中斷洪燥,由于沒有使用中斷,所以這里將閾值設(shè)置為1即可续徽。
UART1->UFCR = (UART1->UFCR & ~UART_UFCR_TXTL_MASK) | UART_UFCR_TXTL(1); //設(shè)置發(fā)送FIFO 閥值
UART1->UFCR = (UART1->UFCR & ~UART_UFCR_TXTL_MASK) | UART_UFCR_TXTL(1); //設(shè)置接收FIFO 閥值

UART1->UCR1 &= ~UART_UCR1_ADBR_MASK; //禁用可變波特率
// UART1->UCR1 |= UART_UCR1_ADBR_MASK;

/*波特率設(shè)置方式 1 蚓曼。 使用官方SDK設(shè)置波特率函數(shù)*/
UART_SetBaudRate(UART1, 115200, 40000000);

/*波特率設(shè)置方式 2 。 手動計(jì)算钦扭,填入寄存器*/
/*設(shè)置串口波特率
* Ref Freq時(shí)鐘 40MHz
* UFCR RFDIV   110  0x06 7分頻    5.714MHz
* BaudRate     115200bps
* UBMR         31-1 = 0x09
* UBIR         10-1 = 0x1E
*/
UART1->UFCR &= ~(0x07 << 7); //清零分頻值
UART1->UFCR |= (0x06 << 7);  //設(shè)置分頻值纫版,40MHz /7 =  5.714MHz

UART1->UBIR = 0x09;
UART1->UBMR = 0x1E;

串口初始化配置了多個(gè)寄存器,結(jié)合代碼和《IMX6ULRM》(參考手冊)53.15 UART Memory Map/Register Definition 章節(jié)可以查看寄存器的詳細(xì)介紹客情。不必花太多時(shí)間在這些寄存器其弊,在需要時(shí)能夠找到即可癞己。

  • 第三部分:使能 UART
    UART 初始化完成以后就可以使能 UART 了,設(shè)置寄存器 UARTx_UCR1 的位 UARTEN 為 1梭伐。
/*開啟串口*/
UART1->UCR1 |= UART_UCR1_UARTEN_MASK;
  • 第四部分:UART 數(shù)據(jù)接收
    串口接收函數(shù)僅實(shí)現(xiàn)簡單的接收字符串功能痹雅,沒有使用中斷。
/*!
 * 功能:官方SDK 串口字符串讀取函數(shù)
 * @brief Reads the receiver register.
 *
 * This function is used to read data from receiver register.
 * The upper layer must ensure that the receiver register is full or that
 * the RX FIFO has data before calling this function.
 *
 * @param base UART peripheral base address.
 * @return Data read from data register.
 */
static inline uint8_t UART_ReadByte(UART_Type *base)
{
    return (uint8_t)((base->URXD & UART_URXD_RX_DATA_MASK) >> UART_URXD_RX_DATA_SHIFT);
}


/*函數(shù)功能:串口接收函數(shù)
 *參數(shù): base,指定串口糊识。data,保存接收到的數(shù)據(jù)绩社。 length,要接收的數(shù)據(jù)長度
 *
*/
void UART_ReadBlocking(UART_Type *base, uint8_t *data, uint8_t length)
{
    while (length--)
    {
        /* 等待接收完成 */
        while (!(base->USR2 & UART_USR2_RDR_MASK))
        {
        }
        /*讀取接收到的數(shù)據(jù) */
        *(data++) = UART_ReadByte(base);
    }
}
  • 第五部分:UART 數(shù)據(jù)發(fā)送
/*!
 * 功能:官方SDK 串口發(fā)送函數(shù)
 * 參數(shù):base赂苗,指定串口愉耙。data,指定要發(fā)送的字節(jié)
 * This function is used to write data to transmitter register.
 * The upper layer must ensure that the TX register is empty or that
 * the TX FIFO has room before calling this function.
 */
static inline void UART_WriteByte(UART_Type *base, uint8_t data)
{
    base->UTXD = data & UART_UTXD_TX_DATA_MASK;
}

/*
 *功能:官方SDK 串口字符串發(fā)送函數(shù)
 *參數(shù)說明:
*/
void UART_WriteBlocking(UART_Type *base, const uint8_t *data, uint8_t length)
{

    while (length--)
    {
        /* Wait for TX fifo valid.
         * This API can only ensure that the data is written into the data buffer but can't
         * ensure all data in the data buffer are sent into the transmit shift buffer.
         */
        while (!(base->USR2 & UART_USR2_TXDC_MASK))
        {
        }
        UART_WriteByte(base, *(data++));
    }
}

13.2 main.c

/uart 下創(chuàng)建 main.c拌滋。

main函數(shù)中實(shí)現(xiàn)“發(fā)送接收到的數(shù)據(jù)”功能朴沿。

#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "pad_config.h"

#include "uart.h"

/*簡單延時(shí)函數(shù)*/
void delay(uint32_t count)
{
    volatile uint32_t i = 0;
    for (i = 0; i < count; ++i)
    {
        __asm("NOP"); /* 調(diào)用nop空指令 */
    }
}

/*提示字符串*/
uint8_t txbuff[] = "Uart polling example\r\nBoard will send back received characters\r\n";
int main()
{
    uint8_t ch; //用于暫存串口收到的字符

    uart_init();
    UART_WriteBlocking(UART1, txbuff, sizeof(txbuff) - 1);

    while (1)
    {
        UART_ReadBlocking(UART1, &ch, 1);
        if (ch == '\r') /*添加回車換行\(zhòng)n\r*/
        {
        UART_WriteBlocking(UART1, '\n', 1);
        }
        if (ch == '\n')
        {
        UART_WriteBlocking(UART1, '\r', 1);
        }
        UART_WriteBlocking(UART1, &ch, 1);
    }

    return 0;
}

十四、編譯下載驗(yàn)證

14.1 編譯代碼

make

執(zhí)行make命令败砂,生成base.bin文件赌渣。

14.2 代碼燒寫

編譯成功后會在當(dāng)前文件夾下生成.bin文件,這個(gè).bin文件也不能直接放到開發(fā)板上運(yùn)行昌犹, 這次是因?yàn)樾枰?bin文件缺少啟動相關(guān)信息坚芜。

為二進(jìn)制文件添加頭部信息并燒寫到SD卡。查看 IMX6ULL學(xué)習(xí)筆記(12)——通過SD卡啟動官方SDK程序

進(jìn)入燒寫工具目錄祭隔,執(zhí)行 ./mkimage.sh <燒寫文件路徑> 命令货岭,例如要燒寫的 base.bin 位于 home 目錄下路操,則燒寫命令為 ./mkimage.sh /home/button.bin疾渴。

執(zhí)行上一步后會列出linux下可燒寫的磁盤,選擇你插入的SD卡即可屯仗。這一步 非常危險(xiǎn)8惆印!魁袜!一定要確定選擇的是你插入的SD卡W椤!峰弹,如果選錯很可能破壞你電腦磁盤內(nèi)容店量,造成數(shù)據(jù)損壞!>铣省融师! 確定磁盤后SD卡以“sd”開頭,選擇“sd”后面的字符即可蚁吝。例如要燒寫的sd卡是“sdb”則輸入“b”即可旱爆。

14.3 實(shí)驗(yàn)現(xiàn)象

使用USB數(shù)據(jù)線連接電腦和開發(fā)板的USB轉(zhuǎn)串口接口舀射,接通電源,打開串口調(diào)試助手怀伦,正常情況下可以串口調(diào)試助手可以收到來自開發(fā)板的提示信息脆烟,通過串口調(diào)試助手發(fā)送字符會立即收到發(fā)送的字符。



? 由 Leung 寫于 2023 年 3 月 29 日

? 參考:12. UART—串口通訊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末房待,一起剝皮案震驚了整個(gè)濱河市邢羔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桑孩,老刑警劉巖张抄,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洼怔,居然都是意外死亡署惯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進(jìn)店門镣隶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來极谊,“玉大人,你說我怎么就攤上這事安岂∏岵” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵域那,是天一觀的道長咙边。 經(jīng)常有香客問我,道長次员,這世上最難降的妖魔是什么败许? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮淑蔚,結(jié)果婚禮上市殷,老公的妹妹穿的比我還像新娘。我一直安慰自己刹衫,他們只是感情好醋寝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著带迟,像睡著了一般音羞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仓犬,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天嗅绰,我揣著相機(jī)與錄音,去河邊找鬼。 笑死办陷,一個(gè)胖子當(dāng)著我的面吹牛貌夕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播民镜,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼啡专,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了制圈?” 一聲冷哼從身側(cè)響起们童,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲸鹦,沒想到半個(gè)月后慧库,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馋嗜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年齐板,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葛菇。...
    茶點(diǎn)故事閱讀 38,697評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甘磨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出眯停,到底是詐尸還是另有隱情济舆,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布莺债,位于F島的核電站滋觉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏齐邦。R本人自食惡果不足惜椎侠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侄旬。 院中可真熱鬧肺蔚,春花似錦、人聲如沸儡羔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汰蜘。三九已至,卻和暖如春之宿,著一層夾襖步出監(jiān)牢的瞬間族操,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留色难,地道東北人泼舱。 一個(gè)月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像枷莉,于是被迫代替她去往敵國和親娇昙。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容