串口

IMX6ULL 8路UART通道,每一路通道都有17个寄存器,其中UART1的寄存器如下图所示,其他通道仅仅是基地值不一样,参考UART1即可。

../../_images/image-20210817104913250.png

硬件分析

  1. UART1接usb转串口,也是默认的调试串口

    ../../_images/image-20210817104737153.png

  2. UART2通过J8跳线帽可接RS485-1或者RS232 CON2

  3. UART3通过J7跳线帽可接RS485-2或者RS232 CON1

    ../../_images/image-20210817105937369.png

    ../../_images/image-20210817105157802.png

  4. UART3还可以单独使用

    ../../_images/image-20210817105225201.png

本章节以UART1为例说明UART的编程

源码分析

参考代码【03.uart_printf】

时钟源选择

参考资料:芯片手册《Chapter 18: Clock Controller Module (CCM)》时钟树得到时钟源为UART_CLK_ROOT

../../_images/image-20210817112033218.png

../../_images/image-20210817111745878.png

从图中可以看出,需要配置CCM_CSCDR1寄存器的UART_CLK_SELUART_CLK_PODF位。CCM_CSCDR1寄存器如下图所示;

../../_images/image-20210817112210441.pngimage-20210817112210441

../../_images/image-20210817112255689.png

  • UART_CLK_SEL设置为0,选择时钟源为pll3_80m

  • UART_CLK_PODF设置为0,选择不分频,那么UART时钟源为80M

UART_CLK_SELUART_CLK_PODF位默认值都为0,所以可以使用默认值。

使能UART时钟

配置CCM_CCGR5寄存器CG12位为11,

../../_images/image-20210817112512329.png

注:从图中看出,CG12位默认值11,所以可以使用默认值。

复用UART引脚

  • IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATAMUX_MODE位 设置为0

  • IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATAMUX_MODE位 设置为0

  • IOMUXC_UART1_RX_DATA_SELECT_INPUTDAISY位设置为11

../../_images/image-20210817113436242.png

../../_images/image-20210817113458760.png

../../_images/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数据

../../_images/image-20210817134734820.png

设置UART1传输格式,波特率

  1. 配置寄存器UART1_UCR2(0x2020084),设置UART1传输格式

    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: 接收数据使能

    ../../_images/image-20210817140500324.png

    ../../_images/image-20210817140513825.png

  2. 配置寄存器UART1_UCR3(0x2020088)

    根据官方文档表示 [RXDMUXSEL]需要设置为1

    UART1->UCR3 |= (1<<2);
    

    ../../_images/image-20210817140914793.png

    ../../_images/image-20210817140924033.png

  3. 寄存器UART1_UFCR(0x2020090)

    UART1_UFCR[9-7]:UART的时钟源分频系数RFDIV 这里配置不分频

    UART1->UFCR = 5 << 7;       /* Uart的时钟clk:80MHz */ 
    

    ../../_images/image-20210817141053260.png

    ../../_images/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

     UART1->UBIR = 71;
     UART1->UBMR = 3124;
    

    ../../_images/image-20210817141334936.png

    ../../_images/image-20210817141344065.png

    ../../_images/image-20210817141353271.png

  5. UART1_UCR1(0x2020080)寄存器,使能UART1

    配置UART1_UCR1[0]:1表示使能UART, 0表示关闭UART。

    Base->UCR1 |= (1 << 0);   /*使能当前串口*/
    

    ../../_images/image-20210817141649937.png

    ../../_images/image-20210817141656707.png

串口发送功能

只有当上一个数据发完的时候,我们才能继续发送,因此需要用到UART1_USR2寄存器中表示UART1发送状态的只读状态位[TXDC]

UART1_USR2[3] : 0表示发送未完成 , 1表示发送已完成

../../_images/image-20210817141804673.png

void putc(unsigned char c)
{
	while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */
	UART1->UTXD = c & 0XFF; 				/* 发送数据 */
}

串口接收功能

UART1_USR2[0] : 0表示没有接收数据就绪, 1表示接收数据准备就绪

../../_images/image-20210817141951944.png

unsigned char getc(void)

{

  while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */

  return UART1->URXD;       /* 返回接收到的数据 */

}

移植printf

上面已经介绍了串口的基本配置,如果我们需要使用printf功能,需要把uboot下的文件文件夹拷贝到工程,如下图所示。

../../_images/image-20210817143145543.png

并且需要用到一些数学库,所以,我们需要链接gcc下面的libgcc库,在makefile添加如下:

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函数