查看芯片手册UART相关内容
支持6路UART
每一个UART内部都有一个接收缓冲区和发送缓冲区,大小为64字节, 如果UART控制器工作在FIFO模式,缓冲区最大为64字节, 如果UART控制器工作在非FIFO模式,缓冲区为1字节, 此时此刻采用非FIFO模式!
UART控制器内部集成了发送器
而发送器内部又集成了发送缓冲区寄存器和发送移位器:
CPU核发送数据的流程:
- 1.CPU核以指针的形式将数据放到发送缓冲区寄存器中进行缓存。
- 2.然后移位器硬件上自动从发送缓冲区中获取数据,然后将数据一位一位的放到TX数据线上。
- 3.移位器发送数据的频率由波特率产生器决定
UART控制器内部还集成了接收器
而接收器内部又集成了接收移位器和接收缓冲区
CPU核接收数据的流程:
- 1.接收移位器硬件上自动从RX数据线上一位一位的将数据收集起来接收数据的频率又波特率产生器决定。
- 2.然后接收移位器硬件上自动将接收的数据放到接收缓冲区中。
- 3.CPU核只需以地址指针的形式访问接收缓冲区寄存器即可,获取接收到的数据 。
波特率配置P969
UBRDIVn和UFRACVALn两个寄存器的值共同决定这波特率的值;
公式:
UBRDIVn+UFRACVALn/16=(SCLK_UART/(bps*16))-1
假设SCLK_UART=40MHz=40000000,将来波特率为115200得到:
UBRDIVn+UFRACVALn/16=(4000000/115200*16)-1 UBRDIVn+UFRACVALn/16=20.7 UBRDIVn=20(取整) UFRACVALn=0.7*16(取整)=11
也就是说:如果将来SCLK_UART=40MHz,并且设置的波特率为115200,那么只需向寄存器UBRDIVn写20,UFRACVALn写11即可。
- SCLK_UART是什么?
- 答:SCLK_UART为UART控制器的时钟源
UART控制器性格的寄存器
ULCON0:配置寄存器
基地址:0xC00A1000
BIT[1:0]=11 //设置数据位为8位
BIT[2]=0 //停止位为1位
BIT[5:3]=000 //不校验
BIT[6]=0 //普通的UART
UCON0:控制寄存器
基地址:0xC00A1004
注意:CPU访问外设的数据三种操作方式:
1.轮训方式(polling)
当CPU访问外设数据时,如果发现外设数据没有
准备就绪,那么CPU就会原地忙等待(死等),
直到外设准备好数据
2.中断方式(interrupt=IRQ=INT)
暂不解释,后面驱动章节会说明
3.DMA方式
暂不解释,后面驱动章节会说明
此时此刻,对UART的数据操作采用轮训方式!
BIT[3:0]=0101 //采用轮训方式 BIT[4]=0 //正常传输数据 BIT[5]=0 //正常传输 #回环模式:自发自收,测试时常用
UTRSTAT0:状态寄存器
基地址:0xC00A1010
切记:CPU向发送缓冲区放数和从接受缓冲区读数的
速度要远远快于发送移位器将数据放到TX
和接受移位器从RX接收数据的速度!
BIT[0]=0:接收缓冲区为空
=1:接受缓冲区有有效数据
CPU判断是否准备就绪的代码:
while(!(UTRSTAT0 & 0x1));
//开始读取数据
BIT[1]=0:发送缓冲区中有数据
=1: 发送缓冲区为空
CPU判断数据是否发送完毕的代码:
while(!(UTRSTAT0 & 0x2));
//开始发下一个数据
UTXH0:发送缓冲区寄存器
基地址:0xC00A1020
BIT[7:0]=保存要发送的数
URXH0:接受缓冲区寄存器
基地址:0xC00A1024
BIT[7:0]=保存接受到的数据
UBRDIV0:波特率配置寄存器你
基地址:0xC00A1028
根据公式进行换算
UFRACVAL0:波特率配置寄存器
基地址:0xC00A102C
根究公式进行换算
UARTCLKENB:UART时钟使能寄存器
基地址:0xC00A9000
BIT[2]=1 //使能UART时钟
UARTCLKGEN0L:UART时钟配置寄存器
基地址:0xC00A9004
BIT[4:2]=000 UART时钟源为CPU的PLL[0]=800MHZ
- 这里需要设置UART_CLK=50MHz
- UART_CLK=50MHZ=800MHZ/n
- =>n=16=CLKDIV0+1=>CLKDIV0=15=0xF
代码示例:
- uart.c
#include "uart.h" //UART初始化函数 void uart_init(void) { //0.禁止UART时钟 //UARTCLKENB[2]=0 UARTCLKENB &= ~(1 << 2); //1.配置UART0使用的两个管脚的功能 //UARTRXD0 //GPIOD_ALTFN0[29:28]=00 GPIOD_ALTFN0 &= ~(0x3 << 28); //GPIOD_ALTFN0[29:28]=01 GPIOD_ALTFN0 |= (1 << 28); //为UARTTXD0 //GPIOD_ALTFN1[5:4]=00 GPIOD_ALTFN1 &= ~(0x3 << 4); //GPIOD_ALTFN1[5:4]=01 GPIOD_ALTFN1 |= (1 << 4); //2.配置UART的工作时钟为50MHz //PLL1(800MHz)/(0xF + 1) = 50MHz //UARTCLKGEN0L[4:2]=000 //UARTCLKGEN0L[12:5]=00000000 UARTCLKGEN0L &= ~((7 << 2) | (0xFF << 5)); //UARTCLKGEN0L[4:2]=001 //UARTCLKGEN0L[12:5]=00001111 UARTCLKGEN0L |= ((1 << 2) | (0xF << 5)); //3.配置UART的工作参数 //波特率115200:根据公式进行配置即可 //数据位8,奇偶校验:None,停止位1 ULCON0 = 3;//115200,8,N,1 UCON0 = 5; //轮循方式 UBRDIV0=26;//50000000/115200*16-1取整 UFRACDIV0=2;//(26.12-26)*16=2.02取整 //4.打开UART时钟,即可使用UART //UARTCLKENB[2]=1 UARTCLKENB |= (1 << 2); } //发送字符函数 void uart_putc(char c) { //1.由于CPU的处理速度远远快与UART //控制器发送数据的速度,发送之前先 //判断发送缓冲区是否为空 //UTRSTAT0[1]=0:有数 //UTRSTAT0[1]=1:空 while(!(UTRSTAT0 & 0x2)); //2.正式发送数据 UTXH0 = c; //将数据塞到发送缓冲区 //3.判断是否需要发回车字符 if (c == '\n') uart_putc('\r'); } //发送字符串函数 //"hello,world" void uart_puts(char *str) { while(*str) { uart_putc(*str); str++; } }
- uart.h
#ifndef __UART_H #define __UART_H /*定义相关的寄存器信息*/ #define ULCON0 (*(volatile unsigned long *)0xC00A1000) #define UCON0 (*(volatile unsigned long *)0xC00A1004) #define UTRSTAT0 (*(volatile unsigned long *)0xC00A1010) #define UTXH0 (*(volatile unsigned long *)0xC00A1020) #define URXH0 (*(volatile unsigned long *)0xC00A1024) #define UBRDIV0 (*(volatile unsigned long *)0xC00A1028) #define UFRACDIV0 (*(volatile unsigned long *)0xC00A102C) #define GPIOD_ALTFN0 (*(volatile unsigned long *)0xC001D020) #define GPIOD_ALTFN1 (*(volatile unsigned long *)0xC001D024) /*UART时钟配置相关寄存器*/ #define UARTCLKENB (*(volatile unsigned long *)0xC000A900) #define UARTCLKGEN0L (*(volatile unsigned long *)0xC000A904) /*声明相关函数*/ //初始化UART函数 extern void uart_init(void); //发送单个字节数据 extern void uart_putc(char c); //发送字符串函数 extern void uart_puts(char *str); #endif
- main.c
#include "uart.h" void main(void) { //初始化UART uart_init(); while(1) { uart_puts("hello,world\n"); } }
- Makefile
#定义变量 NAME=uart_demo BIN=$(NAME).bin ELF=$(NAME).elf OBJ=main.o uart.o CROSS_COMPILE=arm-cortex_a9-linux-gnueabi- CC=$(CROSS_COMPILE)gcc LD=$(CROSS_COMPILE)ld OBJCOPY=$(CROSS_COMPILE)objcopy CP=cp RM=rm INSTALLPATH=/tftpboot #链接选项 LDFLAGS=-nostdlib -nostartfiles -emain #编译选项 CFLAGS=-nostdlib #定义编译规则 #shell.bin:shell.elf # arm...objcopy -O binary shell.elf shell.bin $(BIN):$(ELF) $(OBJCOPY) -O binary $(ELF) $(BIN) $(CP) $(BIN) $(INSTALLPATH) #shell.elf:main.o uart.o led.o strcmp.o # arm...ld -nostartfiles -nostdlib -... -o shell.elf main.o ... $(ELF):$(OBJ) $(LD) $(LDFLAGS) -o $(ELF) $(OBJ) #各种.o:各种.c # 各种编译 %.o:%.c $(CC) $(CFLAGS) -c -o $@ $< #伪目标 #当执行make clean时,仅仅执行clean伪目标对应的命令 clean: $(RM) $(BIN) $(ELF) $(OBJ)