10 IMX6ULL裸机开发:程序示例(UART编程)

创建时间:2022/1/14 13:27
更新时间:2022/2/11 22:41
作者:gi51wa2j
标签:100ask_IMX6ULL_v11, bingo, 操作, 正文

写代码前需要先明白自己要做什么
1小时思考10分钟写代码的模式,而不是焦头烂额的什么都不懂就开始敲写代码

一、串口编程步骤

1.1 看原理图确定UART引脚

  • 有很多串口,使用哪一个?看原理图确定
原理图:网盘开发板配套资料“05_Hardware (原理图)/Base_board/100ask_imx6ull_v1.1.pdf”。
  • 100ASM IMX6ULL的UART1接到一个USB串口芯片,然后就可以通过USB线连接电脑了
  • 上图中的USART1_RX、USART1_TX,接到了PA9、PA10
从上图可知,采用的UART转USB的方案,使用的是UART1。查看IMX6ULL芯片手册《Chapter 55 Universal Asynchronous Receiver/Transmitter(UART)》中涉及关键字UART1的寄存器并设置它。

1.2 配置引脚为UART功能

  • 至少用到发送、接收引脚:txd、rxd

  • 需要把这些引脚配置为UART功能,并使能UART模块

1.3 设置串口参数

  • 有哪些参数?

    • 波特率

    • 数据位

    • 校验位

    • 停止位

  • 示例: 比如15200,8n1表示波特率为115200,8个数据为,没有校验位,1个停止位

1.4 根据状态寄存器读写数据

  • 肯定有一个数据寄存器,程序把数据写入,即刻通过串口向外发送数据

  • 肯定有一个数据寄存器,程序读取这个寄存器,就可以获得先前接收到的数据

  • 很多有状态寄存器

    • 判断数据是否发送出去?是否发送成功?

    • 判断是否接收到了数据?

二、IMX6ULL串口操作

代码:GIT下载后在“10_裸机开发/01_100ASK_IMX6ULL裸机程序/8_UART串口编程/001_uart_txd_char”目录下。
寄存器配置共分为4步骤,本小节中的代码都在程序文件uart.c中。

2.1时钟操作:配置使能UART1时钟|UART特殊时钟

配置并使能UART1时钟,参考资料:芯片手册《Chapter 18: Clock Controller M  odule (CCM)》。

2.1.1 配置UART模块的时钟(寄存器:CCM_CSCDR1)

根据上图可知,我们需要设置CCM_CSCDR1 [UART_CLK_SEL]CCM_CSCDR1  [UART_CLK_PODF]。
通过CSDR1寄存器为UART提供总时钟
     由上图CCM_CSCDR1寄存器,我们可以了解到
CCM_CSCDR1 [UART_CLK_SEL]默认值为0;CCM_CSCDR1 [UART_CLK_PODF]默认值为0

     我们一般选择 pll3_80m 作为 UART 的时钟源,UART_CLK_PODF分频系数选1分频(不分频),最后得到UART的时钟频率为80MHz。

     正好默认值都为0满足我们的时钟需求,所以后续的编程实验,串口时钟这部分可以不设置,用默认值就可以了。

2.1.2 使能UART模块的时钟(寄存器:CCM_CCGR5)

上一步为UART选择了总时钟,这一步需要为UART模块提供时钟
由上图CCM_CCGR5寄存器,我们可以了解到CCM_CCGR5[CG12] 的默认值为11。
参考章节《4.2.6 CCM用于设置是否向GPIO模块提供时钟》我们了解到11表示该模块全程使能,使用默认值,无需设置。
因此时钟这块我们都不需要配置,直接使用默认值即可。

2.1.3 UART特殊时钟

查看芯片手册上对于UART的时钟需求
芯片手册上对于该模块时钟和特殊时钟的要求
  • 手册截图(55.4.2.1(P3571))
  • 重点翻译
    注意这边使用的两个时钟
其中对于FIFO,我们转去相关查看寄存器芯片手册中的描述
     查看2.3.3节中的寄存器描述,我们得知,这个TxFIFO和RxFIFO是默认设置好的,所以我们对module_clock进行再一次的分频参数设置,具体寄存器的设置请查看2.3.3节配置。

2.2 配置引脚功能:复用相关GPIO为UART1功能

     复用相关GPIO为UART1功能,参考资料:芯片手册《Chapter 32: IOMUX Controller  (IOMUXC)》。
     根据前面硬件连接讲解,我们知道串口通信精简接法需要3根信号线,其中GND已由硬件连接了,所以接下来我们需要配置剩余的两个GPIO引脚 (UART1_TXD与UART1_RXD)。

2.2.1 配置UART1_TX复用功能

寄存器:IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA。
     由上图我们得知IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA[MUX_MODE]的默认值为0101,因此我们需要将其改为0,用于UART_TX功能。

2.2.2 配置UART1_RX复用功能

     寄存器:IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA。
     由上图我们得知IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA[MUX_MODE]的默认值为0101,因此我们需要将其改为0,用于UART_RX功能。(程序文件:uart.c)
IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA       = (volatile unsigned int  *)(0x20E0084); IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA       = (volatile unsigned int  *)(0x20E0088); *IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA = 0; *IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA = 0;
设置“Daisy Chain select”
着重提醒:IMX6ULL特殊的地方,IMX6ULL中,可能会有多个引脚都可以驱动某个模块,还需要进一步选择。
查看芯片手册1474页,32.3.3
翻译如下:
    在某些情况下,多个引脚可以同时驱动单个模块输入引脚,这种情况下就需要添加一个级别IOMUXing寄存器。
所有的信号都是多路复用的,所以需要一个专门的软件控制寄存器来控制这些多路复用器来选择输入路径。具体操作如下:   
  • 选择该引脚的复用功能(可通过IOMUXC_SW_MUX_CTL_<PAD>寄存器)
  • 选择各多路复用器的输入路径(可通过Daisy Chain registers)
     比如下图中:A、B、C三个引脚都可以设置为工作于Module X,它们都可以驱动Module  X。但是使用哪一个引脚呢?还需要设置“Daisy Chain select”,用来选择A、B、C之一。
     对于UART1_RX引脚,我们除了设置IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA让它工作于ALT0之外,还需要设置寄存器IOMUXC_UART1_RX_DATA_SELECT_INPUT,如下图所示:
     在上图中,引脚GPIO1_IO02设置为ALT8模式时,它也可以用作UART1的RX输入引脚;引脚UART1_RX设置为ALT0模式时,它也可以用作UART1的RX输入引脚。就是说你所用到的模块具体输入引脚在这还需要再进行选择。
使用的模块是否有需要进行该寄存器的选择,得看芯片手册上有没有自己的所用到的模块,注意这些最后后缀为INPUT的,在里面选择所使用的引脚输入。(该部分截图来自芯片手册IOMUX部分)


UART1_RX为何可以选择那么多引脚?请看下图:
  • UART1的发送引脚有2个选择:GPIO1_IO02、UART1_TX,假设选择了UART1_TX作为发送引脚。
  • UART1的接收引脚有2个选择:GPIO1_IO03、UART1_RX,假设选择了UART1_RX作为接收引脚。
     但是UART1的模块还可能通过daisy引脚再次选择:GPIO1_IO02、GPIO1_IO03、UART1_TX、UART1_RX中的某一个。比如上图中,选择UART1_TX时,这就构成了回环,可以用于测试:IMX6ULL发出去的数据直接返回给自己;选择UART_RX是,就可以接收外面设备比如PC机的数据。
     在daisy中选择哪一个引脚连接到UART1模块?还需要进一步设置,代码如下(我们选择引脚UART1_RX):
IOMUXC_UART1_RX_DATA_SELECT_INPUT               = (volatile unsigned int  *)(0x20E0624); *IOMUXC_UART1_RX_DATA_SELECT_INPUT = 3;

2.2.3 配置UART1_TX硬件参数

寄存器:IOMUXC_SW_PAD_CTL_PAD_UART1_TX_DATA。
参考章节03 IMX6ULL裸机开发:普适的GPIO引脚操作方法概述,经过比对,我们使用默认值(0x10b0)即可,无需配置。
打开芯片手册,找到该寄存器
该寄存器的理解可以参考:https://blog.csdn.net/u010168781/article/details/77855666
默认值为10110000其中

2.2.4 配置UART1_RX硬件参数

寄存器:IOMUXC_SW_PAD_CTL_PAD_UART1_RX_DATA。
参考章节03 IMX6ULL裸机开发:普适的GPIO引脚操作方法概述, 经过比对,我们使用默认值(0x10b0)即可,无需配置。
(大致同上)

2.3 设置串口参数

2.3.1 设置数据格式

配置寄存器UART1_UCR2(0x2020084)

设置UART1传输格式
UART1->UCR2 |= (1<<14) |(1<<5) |(1<<2)|(1<<1);
[14]:   1:忽略RTS引脚
[8] :   0: 关闭奇偶校验     默认为0,无需设置
[6] :   0: 停止位1位        默认为0,无需设置
[5] :   1: 数据长度8位
[2] :   1: 发送数据使能
[1] :   1: 接收数据使能

2.3.2 IMX6ULL芯片要求必须设置

配置寄存器UART1_UCR3(0x2020088)

根据官方文档表示 [RXDMUXSEL]需要设置为1。
[2]:  1:IM6ULL的UART用了这个MUXED模型,因此这一位需要置位为1   
设置串口模型
UART1->UCR3 |= (1<<2);

2.3.3 设置UART模块状态机的时钟

寄存器UART1_UFCR(0x2020090)


UART1_UFCR[9-7]:UART的时钟源分频系数
我们选择101(即十进制的5),表示不分频。
UART1->UFCR = 5 << 7;       /* Uart的时钟clk:80MHz */

2.3.4 设置波特率

寄存器UART1_UBIR(0x20200A4), UART1_UBMR(0x20200A8)
通过设置UART1_UBIR与UART1_UBMR,最终确定波特率。
IMX6ULL波特率计算公式:
a.设置115200的波特率即BaudRate = 115200;
b.UART1的时钟频率前面内容已确定80Mhz即Ref Freq = 80000000;
c.IMX6ULL波特率计算公式得115200 = 80000000 /(16*(UBMR + 1)/(UBIR+1));
d.选取一组满足上式的参数:UBMR、UBIR即可;
e. UART1_UBIR = 71  ; UART1_UBMR = 3124
 UART1->UBIR = 71;
UART1->UBMR = 3124;

2.4 使能UART1

虽然给UART提供了时钟,但是UART本身并未使能,需要设置以下寄存器:
使能UART1,UART1_UCR1(0x2020080)寄存器如下:
配置UART1_UCR1[0]:1表示使能UART, 0表示关闭UART。
Base->UCR1 |= (1 << 0);        /*使能当前串口*/

2.5 根据状态寄存器读写数据

2.5.1 状态寄存器

2.5.2 接收数据寄存器

(和发送数据寄存器一样,是移植printf的关键)

读这个寄存器,就可读取串口数据,如下图:

2.5.3 发送数据寄存器

三、代码附件

uart.zip
Makefile文件中GCC 选项-nostdlib作用
简要阐述该参数作用:避免C编译器链接电脑上的启动文件
详情查看该链接:https://blog.csdn.net/clb1609158506/article/details/41381561