10.串口
串口框图:

UART_URXD寄存器保存着串口接收到的数据。
UART_UTDX寄存器为发送数据寄存器,如果需要通过串口发送数据,只需要将数据写入到UART_UTXD寄存器里面、
UART_UCR1~UART_UCR4寄存器都是串口控制寄存器,
UART_UCR1[0]是UART的使能位,为1使能
UART_UCR1[4]:自动检测波特率使能位,为1使能波特率自动检测
UART_UCR2[0]:软件复位位,为0的时候复位UART。
UART_UCR2[2]:发送使能位,设置为1
UART_UCR2[5]:设置数据位 0 表示7位数据位 1表示 8位数据位
UART_UCR2[6]:设置停止位 0表示1位停止位 1表示2位停止位
UART_UCR2[7]:表示奇偶校验位,为0是偶校验、为1为奇校验
UART_UCR2[8] : 校验使能位,为0关闭校验位
UART_UCR3的[2]必须为1!!!
UART_UFCR寄存器的[7-9]设置分频值,时钟源为PLL3 我们设置为6-1即6分频 分频后为80Mhz

UART前面还可以对时钟源进行设置,保持默认即可。
CCM_CSCDR寄存器UART_CLK_SEL位可以对UART的时钟源进行选择 0:PLL3经过6分频(前面设置的)后的80Mhz以及 1:OSC(24Mhz)时钟源
CCM_CSCDR寄存器UART_CLK_PODF位控制预分频值,一半设置为1分频
UART_UFCR、UART_UBIR、UART_UBMR这三个寄存器可以配置串口的波特率
UART_USR2寄存器的bit0为1 的时候表示有数据可以读取。bit3为1的时候表示数据发送完成。
bsp_uart.h
c
#ifndef __BSP_UART_H
#define __BSP_UART_H
#include "imx6ul.h"
void uart_init(void);
void uart_disable(UART_Type *base);
void uart_enable(UART_Type *base);
void uart_softReset(UART_Type *base);
void uart_io_init(void);
void putc(unsigned char c);
unsigned char getc(void);
void puts(char *str);
void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz);
#endif
bsp_uart.c
c
#include "imx6ul.h"
#include "bsp_led.h"
#include "bsp_delay.h"
#include "bsp_gpio.h"
#include "bsp_uart.h"
void uart_init(void){
uart_io_init();
uart_disable(UART1);
uart_softReset(UART1);
//配置uart1
UART1->UCR1 = 0;
//配置uart的数据位
UART1->UCR2 |= (1<<1)|(1<<2)|(1<<5)|(1<<14);
//初始化
UART1->UCR3 |= (1<<2);
/*
* 设置UART的UCR1寄存器,关闭自动波特率
* bit14: 0 关闭自动波特率检测,我们自己设置波特率
*/
UART1->UCR1 &= ~(1<<14);
//设置波特率
#if 0
UART1->UFCR &=~ (7<<7); //先清零
UART1->UFCR |= (5<<7); //设置为一分频
UART1->UBIR = 71;
UART1->UBMR = 3124;
#endif
uart_setbaudrate(UART1,115200,80000000);
//使能串口
uart_enable(UART1);
}
void uart_disable(UART_Type *base){
base->UCR1 &=~(1<<0);
}
void uart_enable(UART_Type *base){
base->UCR1 |= (1<<0);
}
void uart_softReset(UART_Type *base){
base->UCR2 &=~ (1<<0);
while ((base->UCR2 & 0x1)== 0);
}
void uart_io_init(void){
//配置IO复用功能
IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX,0);
IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX,0);
IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX,0x10B0);
IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX,0x10B0);
}
void putc(unsigned char c){
while(((UART1->USR2 >>3) & 0x1) == 0);//等待上次发送完成
UART1->UTXD = c & 0xff;
}
unsigned char getc(void){
while (((UART1->USR2) & 0x1) == 0) ; //等待有数据可以读取
return UART1->URXD;
}
void puts(char *str){
char *p = str;
while (*p)
{
putc(*p++);
}
}
//由官方库函数提供。
/*
* @description : 波特率计算公式,
* 可以用此函数计算出指定串口对应的UFCR,
* UBIR和UBMR这三个寄存器的值
* @param - base : 要计算的串口。
* @param - baudrate : 要使用的波特率。
* @param - srcclock_hz :串口时钟源频率,单位Hz
* @return : 无
*/
void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int 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 << 4;
divisor = 1;
while (denominator != 0)
{
divisor = denominator;
denominator = numerator % denominator;
numerator = divisor;
}
numerator = srcclock_hz / divisor;
denominator = (baudrate << 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) ? (baudDiff - baudrate) : (baudrate - baudDiff);
if (baudDiff < (baudrate / 100) * 3)
{
base->UFCR &= ~UART_UFCR_RFDIV_MASK;
base->UFCR |= UART_UFCR_RFDIV(refFreqDiv);
base->UBIR = UART_UBIR_INC(denominator - 1); //要先写UBIR寄存器,然后在写UBMR寄存器,3592页
base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
//base->ONEMS = UART_ONEMS_ONEMS(srcclock_hz / (1000 * divider));
}
}
void raise(int sig_nr){
}
mian.c
c
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_uart.h"
int main(void){
int_Init(); //中断初始化必须要开头
imx6u_clkinit();
delay_init();
uart_init();
clk_enable();
while (1)
{
puts("请输出一个字符");
a = getc();
putc(a);
puts("\r\n");
puts("您输入的字符为:");
putc(a);
puts("\r\n\r\n");
}
return 0;
}
- putc()函数为内置函数,要使用它需要在编译时加上 -fno-builtin
修改Makefile
makefile
$(SOBJS) : obj/%.o : %.S
$(CC) -Wall -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
$(CC) -Wall -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $<
即可编译成功。
移植官方stdio,重写printf()函数
- 编译 C 文件的时候添加了选项"-Wa,-mimplicit-it=thumb",否则的话会有如下类似的错误提示:thumb conditional instruction should be in IT block -- `addcs r5,r5,#65536
将官方stdio文件夹放置工程下,需要提供上面两个函数putc() 、getc()
添加头文件路径
然后就可以使用c语言的标准库函数进行格式化输入和输出了。
出现乱码需要检查,编码格式和波特率
移植的printf()禁止使用浮点计算和输出