ARM裸板开发——UART通信方式及使用(二)

简介: ARM裸板开发——UART通信方式及使用


查看芯片手册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)



相关文章
|
6月前
|
算法 编译器 Linux
【Qt4 部署】ARM系统上使用Qt 4 进行开发的QWS 等环境变量部署
【Qt4 部署】ARM系统上使用Qt 4 进行开发的QWS 等环境变量部署
119 0
|
3月前
|
编解码 安全 Linux
基于arm64架构国产操作系统|Linux下的RTMP|RTSP低延时直播播放器开发探究
这段内容讲述了国产操作系统背景下,大牛直播SDK针对国产操作系统与Linux平台发布的RTMP/RTSP直播播放SDK。此SDK支持arm64架构,基于X协议输出视频,采用PulseAudio和Alsa Lib处理音频,具备实时静音、快照、缓冲时间设定等功能,并支持H.265编码格式。此外,提供了示例代码展示如何实现多实例播放器的创建与管理,包括窗口布局调整、事件监听、视频分辨率变化和实时快照回调等关键功能。这一技术实现有助于提高直播服务的稳定性和响应速度,适应国产操作系统在各行业中的应用需求。
100 3
|
6月前
|
Linux 开发工具 芯片
玩转 PI 系列 - 如何在 Rockchip Arm 开发板上安装 Docker Tailscale K3s Cilium?
玩转 PI 系列 - 如何在 Rockchip Arm 开发板上安装 Docker Tailscale K3s Cilium?
|
6月前
|
存储 Ubuntu Linux
ARM-Linux开发与MCU开发的不同之处分析
ARM-Linux开发与MCU开发的不同之处分析
66 0
|
Linux C++
基于ARM-contexA9-Linux驱动开发:如何获取板子上独有的ID号
基于ARM-contexA9-Linux驱动开发:如何获取板子上独有的ID号
124 0
|
芯片
基于友善之臂ARM-ContexA9-ADC驱动开发
基于友善之臂ARM-ContexA9-ADC驱动开发
92 0
|
缓存 C++
基于ARM-contexA9-蜂鸣器pwm驱动开发
基于ARM-contexA9-蜂鸣器pwm驱动开发
104 0
|
18天前
|
编解码 弹性计算 应用服务中间件
阿里云服务器Arm计算架构解析:Arm计算架构云服务器租用收费标准价格参考
阿里云服务器架构分为X86计算、Arm计算、高性能计算等多种架构,其中Arm计算架构以其低功耗、高效率的特点受到广泛关注。本文将深入解析阿里云Arm计算架构云服务器的技术特点、适用场景以及包年包月与按量付费的收费标准与最新活动价格情况,以供选择参考。
|
22天前
|
存储 Docker 容器
ARM架构鲲鹏主机BClinux离线安装docker步骤
下载并安装适用于ARM架构的Docker CE二进制文件,解压后移动至/usr/bin目录。创建docker组,配置systemd服务脚本(docker.service、docker.socket、containerd.service),重载systemd配置,启动并启用docker服务。编辑daemon.json配置存储驱动、镜像加速地址等,最后拉取所需镜像。
37 0
|
27天前
|
NoSQL MongoDB Docker
求助,有没有大神可以找到arm64架构下mongodb的3.6.8版本的docker镜像?
在Docker Hub受限的情况下,寻求适用于ARM架构的docker镜像资源或拉取链接,以便在x86架构上获取;内网中的机器为ARM架构,因此优先请求适合ARM的Docker镜像或Dockerfile,非常感激您的帮助。