引言: 从本文开始, 本个专题之后的几篇文章都是讲解嵌入式开发中几种常见的通信协议的, 比如UART, I2C,SPI, CAN总线这些我就不讲了, 没用到过, 学是学不完的, 等用到的时候再去学习, 毕竟嵌入式开发有一句话, 技术够用就行。本文涵盖了UART的基础知识, 根据SOC的参考手册就行裸机编程事项uart的功能。
一、UART基础知识
Universal Asynchronous Receiver Transmitter 即 通用异步收发器,是一种通用的串行、异步通信总线 该总线有两条数据线,可以实现全双工的发送和接收在嵌入式开发中常常用来外设与主机就行通信。
关于uart的基础知识, 在HAL库下使用uart的那篇文章有, 这里我就不做更详细的说明了。需要注意的UART之所以一次只发送一个字节的数据是因为因为uart是异步通信的, 这样可以减少误差的积累导致的数据错乱。
还有一个小问题, 大家有没有想过, 当出现一下这种情况的时候该怎么区分是一个1还是多个1或者是0还是多个0:
答案是根据时钟来确定高低电平时候的1或者0的个数, 也就是常听说的掐时钟。
二、UART的寄存器编程
在这里, 我给大家演示一下不使用各个库如何开发出uart的程序, 大家搞明白这个原理之后, 大家也就明白我们在开发stm32的时候使用的标准库和HAL的原理了, 他们就是根据芯片手册, 根据各个外设的存储器映射表对地址做了一下封装。
提升串口信号的抗干扰能力和通信的距离RS232协议,485协议, 在uart的标准上对电气特性做了加强, 抗干扰能力更强了,但是在软件编程上没有啥子区别的, 大家别担心。
我们调用的各个库里面的函数的实现原理:
#ifndef ___EXYNOS4412_H_
#define ___EXYNOS4412_H_
#define __REG(x) (*(volatile unsigned int *)(x))
/****************EXINT***************/
#define EXT_INT1_CON __REG(0X11400700)
#define EXT_INT2_CON __REG(0X11400704)
#define EXT_INT3_CON __REG(0X11400708)
#define EXT_INT4_CON __REG(0X1140070C)
#define EXT_INT5_CON __REG(0X11400710)
#define EXT_INT6_CON __REG(0X11400714)
#define EXT_INT7_CON __REG(0X11400718)
#define EXT_INT8_CON __REG(0X11400700)
#define EXT_INT9_CON __REG(0X11400700)
#define EXT_INT10_CON __REG(0X11400700)
#define EXT_INT11_CON __REG(0X11400700)
#define EXT_INT12_CON __REG(0X11400700)
大家看以上代码, 0X11400700这些一系列的数字其实是各个寄存器的地址, 特殊功能寄存器, 我们可以讲这些地址封装成一个宏, 增加可读性,接着我们就可以使用c语言强大的功能, 使用指针对指定的地址进行读写操作。
uart的寄存器编程:
#include "exynos_4412.h"
void UART_Init(void)
{
/*1.将GPA1_0和GPA1_1设置成UART2的接收和发送引脚 GPA1CON[7:0]*/
GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);
/*2.设置UART2的帧格式 8位数据位 1位停止位 无校验 正常模式 ULCON2[6:0]*/
UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/
UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);
/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
UART2.UBRDIV2 = 53;
UART2.UFRACVAL2 = 4;
}
void UART_Send_Byte(char Dat)
{
/*等待发送寄存器为空,即上一个数据已经发送完成 UTRSTAT2[1]*/
while(!(UART2.UTRSTAT2 & (1 << 1)));
/*将要发送的数据写入发送寄存器 UTXH2*/
UART2.UTXH2 = Dat;
}
char UART_Rec_Byte(void)
{
char Dat = 0;
/*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
if(UART2.UTRSTAT2 & 1)
{
/*从接收寄存器中读取接收到的数据 URXH2*/
Dat = UART2.URXH2;
return Dat;
}
else
{
return 0;
}
}
void UART_Send_Str(char * pstr)
{
while(*pstr != '\0')
UART_Send_Byte(*pstr++);
}
int main()
{
char RecDat = 0;
UART_Init();
while(1)
{
/*
RecDat = UART_Rec_Byte();
if(RecDat == 0)
{
}
else
{
RecDat = RecDat + 1;
UART_Send_Byte(RecDat);
}
*/
/*
UART_Send_Str("Hello World\n");
*/
printf("Hello World\n");
}
return 0;
}
以上代码大家不需要看懂, 在实际开发中我相信这样开发的很少, 大家只需要明白我们使用的不标准库, 还是HAL库,里面的函数的本质还是对寄存器进行读写, 只不过这些库将这些寄存器封装了一层又一层。
三、UART在HAL库, 标准库中的使用
这些内用在本专题的hal库下使用uart下有, 我就不重复了。