第五站,我想对社会说——uart串口程序分析
我们存在的价值,是得到这个世界的认可,是让世界认识你,知道你的存在,我们内心的故事,也需要向别人倾述,否则谁能轻易了解你?我们的苦闷也需要偶尔的牢骚,如果我们与世界失去了联系和交际,简直太过单调。那么,就让我们通过UART去倾听ARM的内心吧。
6410提供了四个独立的异步串行I/O端口,每个一步串行I/O端口通过终端或者直接存储器存取(DMA)模式来操作。换句话说就是UART通过产生中断或DMA请求在CPU和UART之间传输数据.
每个UART的通道包含了连个64字节收发FIFO存储器。
每个UART包含一个波特率发生器,发送器,接收器和控制单元,该波特率发生器由PCLK,ext_uclk0或ext_uclk1进行时钟控制。
发射器和接收器包含64字节的FIFO存储器和数据移位寄存器,发送数据之前,首先将数据写入FIFO存储器,然后复制到发送移位寄存器,通过发送数据的引脚(txdn)将数据发送,
同时,通过数据接收的引脚(rsdn)将接收到的数据从接收移位寄存器复制到FIFO存储器。这其中的复制,无需要我们人工干涉,硬件自动完成。
每个UART的通道包含了连个64字节收发FIFO存储器。
每个UART包含一个波特率发生器,发送器,接收器和控制单元,该波特率发生器由PCLK,ext_uclk0或ext_uclk1进行时钟控制。
发射器和接收器包含64字节的FIFO存储器和数据移位寄存器,发送数据之前,首先将数据写入FIFO存储器,然后复制到发送移位寄存器,通过发送数据的引脚(txdn)将数据发送,
同时,通过数据接收的引脚(rsdn)将接收到的数据从接收移位寄存器复制到FIFO存储器。这其中的复制,无需要我们人工干涉,硬件自动完成。
我们要做的,就只是设置好波特率等工作,然后将数据写进发送FIFO,硬件自动会拷贝数据到发送移位寄存器发送我们的数据,读数据只需要当接收完成我们读取接收FIFO中的数据就可以了。
regs.h:
#ifndef __REGS_H#define __REGS_H
/******************uart****************/#define GPACON (*(volatile unsigned int *)0x7F008000)#define ULCON0 (*(volatile unsigned int *)0x7F005000) //UART通道0行控制寄存器#define UCON0 (*(volatile unsigned int *)0x7F005004) //UART通道0控制寄存器#define UFCON0 (*(volatile unsigned int *)0x7F005008) //UART通道0FIFO控制寄存器#define UMCON0 (*(volatile unsigned int *)0x7F00500C) //UART通道0调制解调器(Modem)控制寄存器#define UTRSTAT0 (*(volatile unsigned int *)0x7F005010) //UART通道0发送/接收状态寄存器#define UERSTAT0 (*(volatile unsigned int *)0x7F005014) //UART通道0接收错误状态寄存器#define UFSTAT0 (*(volatile unsigned int *)0x7F005018) //UART通道0FIFO状态寄存器#define UMSTAT0 (*(volatile unsigned int *)0x7F00501C) //UART通道0调制解调器(Modem)状态寄存器#define UTXH0 (*(volatile unsigned int *)0x7F005020) //UART通道0发送缓冲寄存器#define URXH0 (*(volatile unsigned int *)0x7F005024) //UART通道0接收缓冲寄存器#define UBRDIV0 (*(volatile unsigned int *)0x7F005028) //UART通道0波特率分频寄存器#define UDIVSLOT0 (*(volatile unsigned int *)0x7F00502C) //UART通道0分频插槽寄存器#define UINTP0 (*(volatile unsigned int *)0x7F005030) //UART通道0中断处理寄存器#define UINTSP0 (*(volatile unsigned int *)0x7F005034) //UART通道0中断源处理器#define UINTM0 (*(volatile unsigned int *)0x7F005038) //UART通道0中断屏蔽寄存器
#endif
uart.c:
#include "common.h"
#include "regs.h"
#include <stdarg.h>
/*va_list
*va_start
*va_end
*va_arg
*/
/*检查上次数据是否发完,参考S3C6410手册P1096*/
#define TR_BUSY while(!(UTRSTAT0 & 2)) //检测UTRSTAT0的第二位是否为0,为0表示发送缓冲寄存器不为空,即有数据,表示发送正忙,咱还不能写数据到此寄存器需等待。
/*检查是否收到数据,参考S3C6410手册P1096*/
#define RC_BUSY while(!(UTRSTAT0 & 1)) //检测UTRSTAT0的第一位是否为0,为0表示接收缓冲寄存器为空,即没有有效数据,此时不能读该寄存器获得接收数据,需等待。
void uart_init(void)
{
/*设置GPA0-1为UART_com0功能,参考S3C6410手册P312*/
GPACON &= ~(0xff);
GPACON |= 0x22;
/*设置8N1,参考S3C6410手册P1091*/
ULCON0 = 3; //这里设置UART行控制器,行控制器用来选择操作模式,设定奇偶校验,设置每帧中停止位个数,数据位的个数等。
//具体设置请看6410数据手册UART章节,这里赋值为3,即设置为:普通操作模式,无校验,每帧一位停止位,数据位个数为8位(就由1-0位控制)
/*设置采用轮询模式,参考S3C6410手册P1092*/
UCON0 |= (1 << 2) | (1 << 0); //这里就是设置第2位,第0位,这两位的设置是设置成轮询模式,其他位默认0,具体请看其他位的设置对应什么。
//位11-10=00,表示使用PCLK作为UART波特率时钟源,位9=0,设置中断请求类型为脉冲,位8=0,设置接收中断请求类型为脉冲,
//其他的请具体看手册吧
/*把FIFO关掉*/
UFCON0 = 0;
UMCON0 = 0;
/*配置波特率为115200,参考S3C6410手册P1101*/
UBRDIV0 = 34;//34.8<--115200
UDIVSLOT0 = 0xdfdd;//13<--0.8 //这里得到用16*0.8=12.8,四舍五入得到13,然后查看手册UART章节表UDIVSLOTn,找到对应13的值0xdfdd
}
波特率的设置过程:
#include "regs.h"
#include <stdarg.h>
/*va_list
*va_start
*va_end
*va_arg
*/
/*检查上次数据是否发完,参考S3C6410手册P1096*/
#define TR_BUSY while(!(UTRSTAT0 & 2)) //检测UTRSTAT0的第二位是否为0,为0表示发送缓冲寄存器不为空,即有数据,表示发送正忙,咱还不能写数据到此寄存器需等待。
/*检查是否收到数据,参考S3C6410手册P1096*/
#define RC_BUSY while(!(UTRSTAT0 & 1)) //检测UTRSTAT0的第一位是否为0,为0表示接收缓冲寄存器为空,即没有有效数据,此时不能读该寄存器获得接收数据,需等待。
void uart_init(void)
{
/*设置GPA0-1为UART_com0功能,参考S3C6410手册P312*/
GPACON &= ~(0xff);
GPACON |= 0x22;
/*设置8N1,参考S3C6410手册P1091*/
ULCON0 = 3; //这里设置UART行控制器,行控制器用来选择操作模式,设定奇偶校验,设置每帧中停止位个数,数据位的个数等。
//具体设置请看6410数据手册UART章节,这里赋值为3,即设置为:普通操作模式,无校验,每帧一位停止位,数据位个数为8位(就由1-0位控制)
/*设置采用轮询模式,参考S3C6410手册P1092*/
UCON0 |= (1 << 2) | (1 << 0); //这里就是设置第2位,第0位,这两位的设置是设置成轮询模式,其他位默认0,具体请看其他位的设置对应什么。
//位11-10=00,表示使用PCLK作为UART波特率时钟源,位9=0,设置中断请求类型为脉冲,位8=0,设置接收中断请求类型为脉冲,
//其他的请具体看手册吧
/*把FIFO关掉*/
UFCON0 = 0;
UMCON0 = 0;
/*配置波特率为115200,参考S3C6410手册P1101*/
UBRDIV0 = 34;//34.8<--115200
UDIVSLOT0 = 0xdfdd;//13<--0.8 //这里得到用16*0.8=12.8,四舍五入得到13,然后查看手册UART章节表UDIVSLOTn,找到对应13的值0xdfdd
}
波特率的设置过程:
UBRDIV0中的值决定串行Rx/Tx时钟波特率,具体计算如下图:
(可怜啊,这有是两幅截图呢,关于波特率的计算,来自于6410datasheetUART章节,请主动查看)
void uputchar(char c)
{
TR_BUSY;
/*把要发送的数据写到发送寄存器,参考S3C6410手册P1100*/
UTXH0 = c;
}
char ugetchar(void)
{
RC_BUSY;
/*硬件会把收到的数据自动放到URXH0里,参考S3C6410手册P1100*/
return URXH0;
}
void _uputs(char *buf)
{
while(*buf != '\0'){
uputchar(*buf);
if(*buf == '\n'){
uputchar('\r');
}
buf++;
}
}
void uputs(char *buf)
{
_uputs(buf);
uputchar('\n');
uputchar('\r');
}
char *ugets(char *buf)
{
char *save = buf;
char c;
while((c = ugetchar()) != '\r'){
*buf = c;
uputchar(c);
buf++;
}
*buf = '\0';
uputchar('\r');
uputchar('\n');
return save;
}
void itoa(unsigned int d, char *buf)
{
int i;
if(d < 10){
buf[0] = d + '0';
buf[1] = '\0';
return;
}
itoa(d / 10, buf);
for(i = 0; buf[i] != '\0'; i++){
;
}
buf[i] = (d % 10) + '0';
buf[i + 1] = '\0';
}
void xtoa(unsigned int d, char *buf)
{
int i;
if(d < 16){
if(d < 10){
buf[0] = d + '0';
buf[1] = '\0';
}else{
buf[0] = d - 10 + 'a';
buf[1] = '\0';
}
return;
}
xtoa(d / 16, buf);
for(i = 0; buf[i] != '\0'; i++){
;
}
if((d % 16) < 10){
buf[i] = (d % 16) + '0';
buf[i + 1] = '\0';
}else{
buf[i] = (d % 16) - 10 + 'a';
buf[i + 1] = '\0';
}
}
/*%s %c %d %x*/
//uprintf("hello %d %c %x %s\n", 10, 'a', 0x100, "d");
//va_start 建立对应关系
//va_end 取消对应关系
//va_list 对应关系变量类型
//va_arg 根据对应关系取出相应的数据
int uprintf(const char *fmt, ...)
{
va_list ap;
char c;
int d;
char *s;
char buf[64];
va_start(ap, fmt);
while(*fmt != '\0'){
if(*fmt == '%'){
fmt++;
switch(*fmt){
case 'c':
c = va_arg(ap, int);
uputchar(c);
break;
case 'd':
d = va_arg(ap, int);
if(d < 0){
uputchar('-');
d = -d;
}
itoa(d, buf);
_uputs(buf);
break;
case 's':
s = va_arg(ap, char *);
_uputs(s);
break;
case 'x':
d = va_arg(ap, int);
xtoa(d, buf);
_uputs(buf);
break;
case '%':
break;
default:
break;
}
}else{
uputchar(*fmt);
if(*fmt == '\n'){
uputchar('\r');
}
}
fmt++;
}
va_end(ap);
}
main.c:
#include "common.h"#include "regs.h"
int main(int argc, char **argv){char c;char buf[1024];
uart_init();c = ugetchar();uputchar(c);
uputs("hello uputs...");ugets(buf);uputs(buf);
uprintf("hello uprintf()\n");uprintf("hello %c %s\n", 'x', "ssssssssss");
uprintf("abcd%d %c %s %x\n", 100, 'b', "hello uprintf", 0x100);
return 0;}
说明:
串口绝对是个重要的东西,本站只是分析了串口初始化的过程,其实串口的还有一些地方没有讲到,那就在需要的时候多方资料学习。
关于本站的任何疑问,请来群交流, ES6 :180631429
入群请务必附加入群请求说明,因为防止广告号入群打广告,给志同道合的群友造成不必要的麻烦,影响我们的心情。
附加信息请包含以下关键字:学习,嵌入式,交流,ES,空间,CSDN,疯子,壳,蓝天,飞雪,骑士……
愿我们一起,共同进步。