# 串口 IMX6ULL 8路UART通道,每一路通道都有17个寄存器,其中UART1的寄存器如下图所示,其他通道仅仅是基地值不一样,参考UART1即可。 ![](media/image-20210817104913250.png) ## 硬件分析 1. UART1接usb转串口,也是默认的调试串口 ![](media/image-20210817104737153.png) 2. UART2通过J8跳线帽可接RS485-1或者RS232 CON2 3. UART3通过J7跳线帽可接RS485-2或者RS232 CON1 ![](media/image-20210817105937369.png) ![](media/image-20210817105157802.png) 4. UART3还可以单独使用 ![](media/image-20210817105225201.png) 本章节以UART1为例说明UART的编程 ## 源码分析 参考代码【03.uart_printf】 ### 时钟源选择 参考资料:芯片手册《Chapter 18: Clock Controller Module (CCM)》时钟树得到时钟源为`UART_CLK_ROOT` ![](media/image-20210817112033218.png) ![](media/image-20210817111745878.png) 从图中可以看出,需要配置`CCM_CSCDR1`寄存器的`UART_CLK_SEL`和`UART_CLK_PODF`位。`CCM_CSCDR1`寄存器如下图所示; ![image-20210817112210441](media/image-20210817112210441.png) ![](media/image-20210817112255689.png) - `UART_CLK_SEL`设置为0,选择时钟源为`pll3_80m` - `UART_CLK_PODF`设置为0,选择不分频,那么UART时钟源为80M **注**:`UART_CLK_SEL`和`UART_CLK_PODF`位默认值都为0,所以可以使用默认值。 ### 使能UART时钟 配置`CCM_CCGR5`寄存器`CG12`位为11, ![](media/image-20210817112512329.png) 注:从图中看出,`CG12`位默认值11,所以可以使用默认值。 ### 复用UART引脚 - `IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA` 的 `MUX_MODE`位 设置为0 - `IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA` 的 `MUX_MODE`位 设置为0 - `IOMUXC_UART1_RX_DATA_SELECT_INPUT`的 `DAISY`位设置为11 ![](media/image-20210817113436242.png) ![](media/image-20210817113458760.png) ![](media/image-20210817133903855.png) 注意:`IOMUXC_UART1_RX_DATA_SELECT_INPUT`解释说明: - 如下图,如果`DAISY`位设置为10(`UART1_TX_DATA_ALT0`),那么就形成下面的链路:UART1发送脚-》UART1_TX--》UART1接收脚 构成回环模式,也就是UART1的发送数据直接返回到UART1接收,可以用来测试。 - 如下图,如果`DAISY`位设置为11(`UART1_RX_DATA_ALT0`)为正常的rx模式,可以接收PC数据 ![](media/image-20210817134734820.png) ### 设置UART1传输格式,波特率 1. 配置寄存器`UART1_UCR2`(0x2020084),设置UART1传输格式 ```c UART1->UCR2 |= (1<<14) |(1<<5) |(1<<2)|(1<<1); ``` - [14]:忽略RTS引脚 - [8] : 0: 关闭奇偶校验 默认为0,无需设置 - [6] : 0: 停止位1位 默认为0,无需设置 - [5] : 1: 数据长度8位 - [2] : 1: 发送数据使能 - [1] : 1: 接收数据使能 ![](media/image-20210817140500324.png) ![](media/image-20210817140513825.png) 2. 配置寄存器`UART1_UCR3`(0x2020088) 根据官方文档表示 [RXDMUXSEL]需要设置为1 ```c UART1->UCR3 |= (1<<2); ``` ![](media/image-20210817140914793.png) ![](media/image-20210817140924033.png) 3. 寄存器`UART1_UFCR`(0x2020090) `UART1_UFCR[9-7]`:UART的时钟源分频系数`RFDIV` 这里配置不分频 ```C UART1->UFCR = 5 << 7; /* Uart的时钟clk:80MHz */ ``` ![](media/image-20210817141053260.png) ![](media/image-20210817141105151.png) 4. 寄存器`UART1_UBIR`(0x20200A4), `UART1_UBMR`(0x20200A8)波特率配置 - 设置115200的波特率即BaudRate = 115200; - UART1的时钟频率前面内容已确定80Mhz即Ref Freq = 80000000; - IMX6ULL波特率计算公式得115200 = 80000000 /(16*(UBMR + 1)/(UBIR+1)); - 选取一组满足上式的参数:UBMR、UBIR即可; - UART1_UBIR = 71 ; UART1_UBMR = 3124 ```c UART1->UBIR = 71; UART1->UBMR = 3124; ``` ![](media/image-20210817141334936.png) ![](media/image-20210817141344065.png) ![](media/image-20210817141353271.png) 5. `UART1_UCR1`(0x2020080)寄存器,使能UART1 配置`UART1_UCR1[0]`:1表示使能UART, 0表示关闭UART。 ```c Base->UCR1 |= (1 << 0); /*使能当前串口*/ ``` ![](media/image-20210817141649937.png) ![](media/image-20210817141656707.png) ### 串口发送功能 只有当上一个数据发完的时候,我们才能继续发送,因此需要用到`UART1_USR2`寄存器中表示UART1发送状态的只读状态位`[TXDC]` ``` UART1_USR2[3] : 0表示发送未完成 , 1表示发送已完成 ``` ![](media/image-20210817141804673.png) ```c void putc(unsigned char c) { while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */ UART1->UTXD = c & 0XFF; /* 发送数据 */ } ``` ### 串口接收功能 ``` UART1_USR2[0] : 0表示没有接收数据就绪, 1表示接收数据准备就绪 ``` ![](media/image-20210817141951944.png) ```c unsigned char getc(void) { while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */ return UART1->URXD; /* 返回接收到的数据 */ } ``` ## 移植printf 上面已经介绍了串口的基本配置,如果我们需要使用printf功能,需要把uboot下的文件文件夹拷贝到工程,如下图所示。 ![](media/image-20210817143145543.png) 并且需要用到一些数学库,所以,我们需要链接gcc下面的libgcc库,在makefile添加如下: ```bash LIBPATH := -lgcc -L /opt/arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/6.2.1 $(TARGET).bin : $(OBJS) $(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH) ``` 如此即可使用printf。 ## 实验 在主程序正常使用printf函数