一、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 Clock
和 Module 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)號順序講解如下:
- 標(biāo)號①選擇 PLL3 時(shí)鐘還是 CCM_PLL3_BYP。我們選擇 PLL3 輸出時(shí)鐘刀脏,寄存器 CCSR[PLL3_SW_CLK_SEL] = 0局荚, 則表示選擇 PLL3 時(shí)鐘。默認(rèn)情況下是這樣設(shè)置的愈污。所以我們代碼中并沒有設(shè)置該寄存器耀态。
- 標(biāo)號②設(shè)置時(shí)鐘分頻,根據(jù)之前的設(shè)置暂雹,PLL3 的輸出頻率為 480MHz 首装,這里的時(shí)鐘分頻是固定的 6 分頻, 經(jīng)過分頻后的時(shí)鐘為
480MHz / 6 = 80MHz
杭跪。- 標(biāo)號③ 再次選擇時(shí)鐘源仙逻。一個(gè)是 PLL3 分頻得到的 80MHz 時(shí)鐘,另外一個(gè)是 OSC 時(shí)鐘即 24MHz 的系統(tǒng)參考時(shí)鐘涧尿。 設(shè)置 CSCDR1[UART_CLK_SEL] = 0系奉,選擇第一個(gè)(80MHz)時(shí)鐘。
- 標(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ù)字蝙泼,遵守以下幾條:
-
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í)鐘】。
-
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)建工程文件夾
- 創(chuàng)建一個(gè)文件夾
uart
- 創(chuàng)建一個(gè)用于存放頭文件的文件夾
include
- 創(chuàng)建一個(gè)用于存放驅(qū)動源碼的文件
device
- 創(chuàng)建一個(gè)啟動文件
start.S
- 創(chuàng)建一個(gè)源文件
main.c
- 創(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):
- 在編譯命令中指明頭文件位置峦剔。
- 使用命令調(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—串口通訊