我是靠谱客的博主 友好马里奥,最近开发中收集的这篇文章主要介绍串口,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1. 串口原理

image

  • UART_URXD寄存器保存着串口接收到的数据
  • UART_UTXD寄存器为发送数据寄存器,如果需要串口发送数据,只需要写入到这个结存器
  • UARTx_UCR1到UARTx_UCR4都是控制寄存器
  • UCR1的bit0是使能位,bit是发送完了产生中断,14是自动监测波特率使能位,为1的时候使能
  • UCR2的bit0位软件复位位,为0的时候复位UART bit1是使能接受,bit2位发送使能,都设置为1,bit4请求发送中断使能(可以不用设置) bit5设置数据位,0是7位,1表示8位,bit6是停止位,0是1个停止位,1表示两位,bit7是奇偶校验位,为0是偶校验,bit8是开不开校验
  • UCR3 bit2必须一直是1
  • UCR4没用到
  • USR2状态寄存器,bit0表示已接收到数据,有数据可以读取,bit0 = 1,要判断;bit3是发送完成,也要判断
  • UART_UFCR的bit7-9设置分频值,在时钟树上,是80Mhz, CSCDRde UART_CLK_SEL设置时钟源,为0就是PLL3的80M,否则是OSC晶振24M;CSCDR1的UART_CLK_PODF用来控制分频,一般为1
  • UART_UFCR、UBIR和UBMR这三个寄存器决定了串口波特率image
    如果是115200的话,自己算吧

2. 步骤

image

3. 代码

bsp_uart.h

#ifndef __BSP_UART_H
#define __BSP_UART_H

#include "imx6ul.h"
/* 函数声明 */
void uart_init(void);
void uart_io_init(void);
void uart_disable(UART_Type *base);
void uart_enable(UART_Type *base);
void uart_softreset(UART_Type *base);
void uart_setbaudrate(UART_Type *base,
                      unsigned int baudrate,
                      unsigned int srcclock_hz);
void putc(unsigned char c);
void puts(char *str);
unsigned char getc(void);
void raise(int sig_nr);
#endif // !__BSP_UART_H

bsp_uart.c

#include "bsp_uart.h"

/*
 * @description : 初始化串口 1,波特率为 115200
 * @param : 无  
 * @return : 无 
 * */
void uart_init()
{
    /* 初始化UART1的IO */
    uart_io_init();

    /* 初始化UART1 */
    /* 关闭串口,前面的也可以先关闭 */
    uart_disable(UART1);        /* 先关闭 UART1 */
    uart_softreset(UART1);      /* 软件复位 UART1 ,复位波特率*/

    UART1->UCR1 = 0;            /* 先清除 UCR1 寄存器 */
    UART1->UCR1 &= ~(1<<14);    /* 关闭自动波特率检测 */

    /* 配置数据位,奇偶校验 */
    /*
     * 设置 UART 的 UCR2 寄存器,设置字长,停止位,校验模式,关闭硬件流控
     * bit14: 1 忽略 RTS 引脚
     * bit8: 0 关闭奇偶校验
     * bit6: 0 1 位停止位
     * bit5: 1 8 位数据位
     * bit2: 1 打开发送
     * bit1: 1 打开接收
     */
    UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);
    UART1->UCR3 |= 1<<2; /* UCR3 的 bit2 必须为 1 */
    /*
     * 设置波特率
     * 波特率计算公式:Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)) 
     * 如果要设置波特率为 115200,那么可以使用如下参数:
     * Ref Freq = 80M 也就是寄存器 UFCR 的 bit9:7=101, 表示 1 分频
     * UBMR = 3124
     * UBIR = 71
     * 因此波特率= 80000000/(16 * (3124+1)/(71+1))
     * = 80000000/(16 * 3125/72) 
     * = (80000000*72) / (16*3125) 
     * = 115200
     */
    UART1->UFCR &= ~(7 << 7);
    UART1->UFCR = 5<<7; /* ref freq 等于 ipg_clk/1=80Mhz z注意是反着来的*/
    UART1->UBIR = 71;
    UART1->UBMR = 3124;

#if 0
    uart_setbaudrate(UART1, 115200, 80000000); /* 设置波特率 */
#endif

    uart_enable(UART1); /* 使能串口 */
}

/*
* @description : 初始化串口 1 所使用的 IO 引脚
* @param : 无
* @return : 无
*/
void uart_io_init()
{
    /* 初始化串口IO  */
    /* 首先是复用为0,就是发送接受 */
    IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);
    IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);
    /* 电气属性,和key一样 */
    IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);
    IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);
}
#if 0
/*
 * @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 / 
    der)/ 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);
        base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
    }
}
#endif
/* 关闭指定uart */
void uart_disable(UART_Type *base)
{
    base->UCR1 &= ~(1 << 0);
}
/* 打开指定uart */
void uart_enable(UART_Type *base)
{
    base->UCR1 |= (1 << 0);
}
/* 复位指定uart */
void uart_softreset(UART_Type *base)
{
    /* 首先将SRST_B清零,UCR2[0] */
    /* 指导SOFTRST为0 */
    base->UCR2 &= ~(1<<0); /* 复位 UART */
    while((base->UCR2 & 0x1) == 0); /* 等待复位完成 */
    /* 重写UBIR和UBMR */
}
/* 发送一个字符 */
void putc(unsigned char c)
{
    while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */
    UART1->UTXD = c & 0XFF; /* 发送数据 */
}
/* 发送一个字符串 */
void puts(char *str)
{
    char *p = str;
    while(*p)
    {
        putc(*p++);
    }
}
/*
* @description : 接收一个字符
* @param : 无
* @return : 接收到的字符
*/
unsigned char getc(void)
{
    while((UART1->USR2 & 0x1) == 0); /* 等待接收完成 */
    return UART1->URXD; /* 返回接收到的数据 */
}
/*
* @description : 防止编译器报错
* @param : 无
* @return : 无
*/
void raise(int sig_nr)
{

}

main

#include "imx6ul.h"
#include "bsp_led.h"
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_exit.h"
#include "bsp_epittimer.h"
#include "bsp_keyfilter.h"
#include "bsp_uart.h"

/*
 * @description	: mian函数
 * @param 		: 无
 * @return 		: 无
 */

int main(void)
{
	char a;
	/* 对于中断的初始化,一定要放在代码的最前面 */
	int_init();
	clk_init();			/* 系统时钟初始化 */
	clk_enable();		/* 使能所有的时钟 			*/
	delay_init();		/* 延时初始化 */
	uart_init();  		/* 初始化串口 */
	led_init();			/* 初始化led 			*/
	beep_init();		/* 初始化beep */
	filterkey_init();	/* 带有消抖功能的按键 */

	while(1)			/* 死循环 				*/
	{	
		puts("请输入一个字符:");
		a = getc();
		putc(a); //回显
		puts("rn");

		puts("您输入的字符为:");
		putc(a);
		puts("rn");
	}
	return 0;
}

4. 问题

putc puts的警告,因为C库函数有这个函数所以编译的时候需要加
-fno-builtinimage
串口linux是UTF-8
有可能串口是乱码,如果设置好了串口还是乱码,说明波特率有问题

uart_setbaudrate报错:
需要在MakeFile中引入数学库

LIBPATH := -lgcc -L /usr/local/arm/gcc-linaro-7.3.1-2018.05-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/7.3.1

解决方法:
thumb conditional instruction should be in IT block -- `addcs r5,r5,#65536'

$(SOBJS) : obj/%.o : %.S
	$(GCC) -Wall -Wa,-mimplicit-it=thumb -fno-builtin -nostdlib -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
	$(GCC) -Wall -Wa,-mimplicit-it=thumb -fno-builtin -nostdlib -c -O2 $(INCLUDE) -o $@ $<

5.printf

移植stdio
main

#include "imx6ul.h"
#include "bsp_led.h"
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_exit.h"
#include "bsp_epittimer.h"
#include "bsp_keyfilter.h"
#include "bsp_uart.h"
#include "stdio.h"

/*
 * @description	: mian函数
 * @param 		: 无
 * @return 		: 无
 */

int main(void)
{
	int a,b;
	/* 对于中断的初始化,一定要放在代码的最前面 */
	int_init();
	clk_init();			/* 系统时钟初始化 */
	clk_enable();		/* 使能所有的时钟 			*/
	delay_init();		/* 延时初始化 */
	uart_init();  		/* 初始化串口 */
	led_init();			/* 初始化led 			*/
	beep_init();		/* 初始化beep */
	filterkey_init();	/* 带有消抖功能的按键 */

	while(1)			/* 死循环 				*/
	{	
		printf("请输入两个数字:");
		scanf("%d %d", &a, &b);
		printf("rn");
		printf("和为:%d", a + b);
		printf("rn");
	}
	return 0;
}

Makefile

# ?= 没有赋值的话就赋值
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET 		  ?= bsp

# :=覆盖之前的值

GCC    			 := $(CROSS_COMPILE)gcc 
LD 				 := $(CROSS_COMPILE)ld
OBJCOPY 		 := $(CROSS_COMPILE)objcopy
OBJDUMP  		 := $(CROSS_COMPILE)objdump 

LIBPATH			 := -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4

# INCDIRS 包含整个工程的.h 头文件目录 是换行符
INCDIRS          := imx6ull 
					bsp/clk 
					bsp/gpio 
					bsp/led 
					bsp/delay 
					bsp/beep 
					bsp/key 
					bsp/int 
					bsp/exit 
					bsp/epittimer 
					bsp/keyfilter 
					bsp/uart 
					stdio/include

# SRCDIRS 包含整个工程的.s .c文件目录

SRCDIRS			 := project 
					bsp/clk 
					bsp/gpio 
					bsp/led 
					bsp/delay 
					bsp/beep 
					bsp/key 
					bsp/int 
					bsp/exit 
					bsp/epittimer 
					bsp/keyfilter 
					bsp/uart 
					stdio/lib

# patsubst的作用是给INCDIRS中的每个目录前面加一个 -I,指明头文件目录时必须加
# -I imx6ull -I bsp/clk -I bsp/led -I bsp/delay 
INCLUDE 		 := $(patsubst %, -I %, $(INCDIRS))

# foreach dir对每个dir都执行一次,SRCDIRS中dir, 即上面的文件夹
# wildcard 取出文件夹内所有文件
# 比如bsp/clk/bsp_clk.c
SFILES 			 := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES 			 := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

# notdir 上面取出来之后去掉目录
SFILENDIR 		 := $(notdir $(SFILES))
CFILENDIR  		 := $(notdir $(CFILES))

# 把所有的.s和.c都变成点o文件,并在前面加上obj
SOBJS  			 := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS 			 := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS 			 := $(SOBJS) $(COBJS)

# 相当于重命名
VPATH 			 := $(SRCDIRS)

.PHONY: clean

# 这段和以前是一样的

$(TARGET).bin : $(OBJS)
	$(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH)
	$(OBJCOPY) -O binary -S $(TARGET).elf $@
	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

# -Wall 编译后显示所有警告
# -nostdlib 不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。
#			这个选项常用于编译内核、bootloader等程序,它们不需要启动文件、标准库文件
# -O2   是比O1更高级的选项,进行更多的优化。
# 		Gcc将执行几乎所有的不包含时间和空间折中的优化。
# 		当设置O2选项时,编译器并不进行循环打开()loop unrolling以及函数内联。
# 		与O1比较而言,O2优化增加了编译时间的基础上,提高了生成代码的执行效率。
# 这样还能带着.h文件一起编译 

$(SOBJS) : obj/%.o : %.S
	$(GCC) -Wall -Wa,-mimplicit-it=thumb -fno-builtin -nostdlib -c -O2 $(INCLUDE) -o $@ $<
$(COBJS) : obj/%.o : %.c
	$(GCC) -Wall -Wa,-mimplicit-it=thumb -fno-builtin -nostdlib -c -O2 $(INCLUDE) -o $@ $<

clean:
	rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

最后

以上就是友好马里奥为你收集整理的串口的全部内容,希望文章能够帮你解决串口所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部