一、UART(串行通信)驱动编程
1.基本概念
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种串行异步收发协议,应用十分广泛。UART工作原理是将数据的二进制位一位一位的进行传输。在UART通讯协议中信号线上的状态位高电平代表’1’低电平代表’0’。当然两个设备使用UART串口通讯时,必须先约定好传输速率和一些数据位。
2.串行通信按传输方向来定义的传输方式:单工/半双工/全双工
3.串行接口标准
RS232
电气特性 (EIA(RS232)电平)
逻辑0 +3~+15v
逻辑1 -3~-15v
机械特性
传输距离 <10m
只能做1对1通信
RS485:工业现场
传输时使用差分信号
传输距离可以超过1000m
可以做1对多通信
TTL电平(用于计算机内部)
逻辑0: 低电平 <0.7v
逻辑1: 高电平 >2.4v
4.数据传输协议
空闲状态为高电平
发送时首先是一个起始位(一个周期的低电平)
发送数据时从LSB(最低值的比特位)开始发送
每帧中数据位的个数5~8bit (变化)
发送可能存在的1bit奇偶校验位 (变化)
发送1~2bit的停止位(高电平)(变化)
波特率: bps(bit per second)(变化)
具体操作流程:
1、电路原理图 (为找到UART对应的管脚)
PC_TXD1 <------ UARTTXD0<------GPIOD18
PC_RXD1 <------ UARTRXD0<------GPIOD14
运行在ARM中的程序,控制UART控制器,再由UART控制器去控制收发数据的管脚,形成UART通信相关的时序,从而简化uart驱动编程
2、cpu datasheet关于 uart 相关内容
GPIOD18, 功能1
GPIOD14, 功能1
以上数据均由查找手册后得知,如下:
GPIODALTFN0 0XC001D020
[29:28] 01, 将GPIOD14管脚设置为功能1
GPIODALTFN1 0XC001D024
[5:4] 01, 将GPIOD18管脚配置为功能1
以上数据均由查找手册后得知,如下:
CPU感知外接硬件状态变化的方式:
1)中断
2)轮询
3)DMA (directly memory access直接存储器访问)
配置:8n1 115200 non-fifo polling
ULCON0 0xc00a1000
[1:0] 11, 8bit数据宽
[2] 0, 1bit停止位
[5:3] 000, 无校验
[6] 0, 正常模式
UCON0 0xc00a1004
[1:0] 01, 接收数据时采用轮询模式接收
[3:2] 01, 发送数据时采用轮询模式发送
[4]=0 , 正常发送数据
[5] 0, 正常模式
UTRSTAT0 0XC00A1010
[0] 0/1 接收缓冲区空/非空
[1] 0/1 发送缓存区非空/空
UTXH0 0xc00a1020
[7:0] 将要发送的字节数据写入其中
URXH0 0xc00a1024
[7:0] 读取接收到的数据
获取 UART 输入的时钟源信息:
UART0CLKENB 0XC00A9000
[2] 0/1 禁止/使能时钟
UART0CLKGEN0L 0XC00A9004
[4:2] 001, 选择PLL1作为UART的时钟源 PLL1=800MHz
[12:5] 时钟分频系数 取值为15 分频后 800M/(15+1) = 50MHz
配置串口波特率:(有公式,直接套)
波特率分频寄存器UBRDIV,UFRACVAL:
UBRDIV0 0XC00A1028
26
UFRACVAL0 0XC00A102C
2 //0.13*16
配置串口波特率公式::
3、编码
main.c
#include "uart.h"
int main(void)
{
/*8n1 115200 polling non-fifo*/
uart_init();
while(1)
{
uart_puts("\nhello esd1909");
}
return 0;
}
uart.h
#ifndef __UART_H__
#define __UART_H__
extern void uart_init(void);
extern void uart_puts(char *);
#endif
uart.c
#define UART0CLKENB *((volatile unsigned int *)0xC00A9000)
#define UART0CLKGEN0L *((volatile unsigned int *)0xC00A9004)
#define GPIODALTFN0 *((volatile unsigned int *)0xC001D020)
#define GPIODALTFN1 *((volatile unsigned int *)0xC001D024)
#define ULCON0 *((volatile unsigned int *)0xC00A1000)
#define UCON0 *((volatile unsigned int *)0xC00A1004)
#define UTRSTAT0 *((volatile unsigned int *)0xC00A1010)
#define UTXH0 *((volatile unsigned int *)0xC00A1020)
#define URXH0 *((volatile unsigned int *)0xC00A1024)
#define UBRDIV0 *((volatile unsigned int *)0xC00A1028)
#define UFRACVAL0 *((volatile unsigned int *)0xC00A102C)
void uart_init(void)
{
/*禁止CLK*/
UART0CLKENB &= ~(1<<2);
/*配置管脚功能*/
GPIODALTFN0 &= ~(3<<28);
GPIODALTFN0 |= (1<<28);
GPIODALTFN1 &= ~(3<<4);
GPIODALTFN1 |= (1<<4);
/*时钟源选择PLL1 800MHz*/
UART0CLKGEN0L &= ~(7<<2);
UART0CLKGEN0L |= (1<<2);
/*第一次分频 800/(15+1)=50MHz*/
UART0CLKGEN0L &= ~(0xff<<5);
UART0CLKGEN0L |= (15<<5);
/*8N1*/
ULCON0 = 0x03;
/*polling*/
UCON0 = 0x05;
/*根据50MHz时钟源 115200bps 进行第二次分频 */
UBRDIV0 = 26;
UFRACVAL0 = 2;
/*使能CLK*/
UART0CLKENB |= (1<<2);
}
void uart_putc(char c)
{
/*轮询发送缓冲区是否为空
*UTRSTAT0[1] 1,空
* */
while(!(UTRSTAT0 & (1<<1))) ;
UTXH0 = c;
if(c == '\n')
uart_putc('\r');
}
/*abcdef*/
void uart_puts(char *str)
{
while(*str)
{
uart_putc(*str);
str++;
}
}
程序里关于\r\n的问题:
windows
\r: 13 (0x0d) 去到行首
\n: 10 (0x0a) 去到下一行
linux: 回车换行只需要 \n
4、交叉编译
arm-cortex_a9-linux-gnueabi-gcc -c -nostdlib main.c -o main.o
arm-cortex_a9-linux-gnueabi-gcc -c -nostdlib uart.c -o uart.o
arm-cortex_a9-linux-gnueabi-ld -nostdlib -nostartfiles -Ttext=0x48000000 -emain main.o uart.o -o uart
arm-cortex_a9-linux-gnueabi-objcopy -O binary uart uart.bin
5、下载运行
cp uart.bin /tftpboot/
在开发板上执行
tftp 48000000 uart.bin
go 48000000