(嵌入式Linux 4 )UART串口

目录

资料来自韦东山嵌入式

硬件介绍

数据包格式

波特率计算

串口框架

S3C2440_UART编程

手动实现printf

手动实验可变参数

字节对齐

自动确定可变参数


硬件介绍

      通用异步收发器简称 UART,全双工方式传输数据,最精简的连线方法只有三根电线:TxD 用于发送数据,RxD 用于接收数据,Gnd 用于给双方提供参考电平。

连接方式:1. 2440使用TTL逻辑电平,而PC使用的是RS232电平,所以加入电平转换芯片将TTL转换为RS232与PC通信。

                  2. 2440外接USB串口芯片,通过USB接入PC。

数据包格式

数据传输流程:

(1)空闲为高电平

(2)当要发送数据时,UART 改变 TxD 数据线的状态(变为 0 状态)并维持 1 位的时间──这
样接收方检测到开始位后,再等待 1.5 位的时间就开始一位一位地检测数据线的状态得到所传输的数据。

(3)UART 一帧中可以有 5、6、7 或 8 位的数据,发送方一位一位地改变数据线的状态将它
们发送出去,首先发送最低位。
(4)如果使用较验功能,UART 在发送完数据位后,还要发送 1 个较验位。有两种较验方法:
奇较验、偶较验──数据位连同较验位中,“1”的数目等于奇数或偶数。
(5)最后,发送停止位,数据线恢复到“空闭”状态(1 状态)。停止位的长度有 3 种:1 位、
1.5 位、2 位。

波特率计算

假设波特率115200,8n1  (数据位8位,无奇偶校验,1位停止位)

每一位传输t=1/115200

传输1Byte需要10位(开始为,数据位,停止位)

则传输1Byte  T=10/115200

则一秒可以传输:1/T=115200/10=11520 Byte

串口框架

串口中存在数据缓存FIFO,移位寄存器。发送数据时,CPU从内存中取得数据,放入FIFO,UART将FIFO中的数据通过移位寄存器一位一位发送出去。接收数据同理。

 

S3C2440_UART编程

修改007_clock
添加usrt.c

void urat0_init()
{
	/*设置引脚用于串口 */
	/*GPH2,3用于TxD0,RxD0*/
	GPHCON &= ~((3<<4)|(3<<6));//清零
	GPHCON |= (2<<4)|(2<<6);

	GPHUP &= ~((1<<2)|(1<<3)); //设置GPH2,3引脚上拉
	/*设置波特率
	*UBRDIVn = (int) (UART clock / (buad rate x 16)) –1
	* UART clock=50M   //前面时钟章节设置的
	* UBRDIVn = (int) (50 000 000 / (115200 x 16)) –1  =26
	*/
	UCON0=0x00000005;   //设置UCON为PCLK,中断/查询模式
	UBRDIV0=26;
	
	/*设置数据格式*/
	ULCON0=0x00000003;	//无校验,1停止位  8位数据长度	8n1
	
	/*其他设置*/
	
}

void putchar(int c)//输出字符
{
	/*UTXH0 UART通道 0 发送缓存寄存器
	*UTRSTAT0  UART通道 0 接收发送状态 寄存器

	*/

	while(!(UTRSTAT0 & (1<<2)));  //等待发送器空
	UTXH0 =(unsigned char)c;   //写入一个字节
}

int getchar(void)
{
	while(!(UTRSTAT0 & (1<<0)));  //等待接收器有数据
	return URXH0;
}

int puts(const char *s)//输出字符串
{
	while(*s)
	{
		putchar(*s);
		s++;
	}
}

添加uart.h
 

#ifndef _UART_H
#define _UART_H

void urat0_init();
void putchar(int c);

int getchar(void);

int puts(const char *s);

#endif

添加main.c,输出helloworld,输出输入的字符

#include "s3c2440_soc.h"
#include "uart.h"

int main(void)
{
	unsigned char c;
	urat0_init();
	puts("Hello,world\n");
	while(1)
	{
		c=getchar();
		if(c=='\r')//\r表示回到行首
		{
			putchar('\n');//\n表示提行
		}
		if(c=='\n')
		{
			putchar('\r');
		}
		putchar(c);
	}
	return 0;
}

修改makefile

all:
	arm-linux-gcc -c -o led.o led.c
	arm-linux-gcc -c -o uart.o uart.c
	arm-linux-gcc -c -o main.o main.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-ld -Ttext 0 start.o led.o uart.o main.o -o uart.elf
	arm-linux-objcopy -O binary -S uart.elf uart.bin
	arm-linux-objdump -D uart.elf > uart.dis
clean:
	rm *.bin *.o *.elf *.dis
	

 

 

手动实现printf

手动实验可变参数

 

printf的输出格式:

格式字符

说明

d

以带符号的十进制形式输出整数(整数不输出符号)

u

以无符号十进制形式输出整数

x

以十六进制无符号形式输出整数(不输出前导符0x),

用x则输出十六进制数的a~f时以小写形式输出

c

以字符形式输出,只输出一个字符

s

输出字符串

 

printf在内存中的变量是以堆栈的方式存放的

如以下代码:

#include <stdio.h>

//int printf(const char *format, ...);
/*
	* 打印出传入参数,format为第一个传入参数,...为可变参数
*/
int push_test(const char *format, ...)
{
	char *p=(char *) &format;
	int i;

	/*(1)将abcd传入format,为了取得后面的数据123,定义一个指针指向format,通过指针移动读取*/
	printf("arg1: %s\n",format);//abcd传入了第一个format
	p=p+sizeof(char *);
	i=*((int *)p);
	printf("arg2:%d\n",i);
	
	return 0;
}

int main(int argc,char **argv)
{
	
	
	printf("sizeof(char   )=%d\n",sizeof(char   ));
	printf("sizeof(int    )=%d\n",sizeof(int    ));
	printf("sizeof(char  *)=%d\n",sizeof(char  *));
	printf("sizeof(char **)=%d\n",sizeof(char **));	
	
	push_test("abcd",123); //(1)		
	return 0;
}	

在内存中的表示为:

注意编译时 gcc -m32 -o push_test push_test.c

输出:

sizeof(char   )=1
sizeof(int    )=4
sizeof(char  *)=4
sizeof(char **)=4
arg1: abcd
arg2:123


加入结构体观察:

#include <stdio.h>

struct person{
	char *name;
	int age;
	char score;
	int id;
};
//int printf(const char *format, ...);
/*
	* 打印出传入参数,format为第一个传入参数,...为可变参数
*/
int push_test(const char *format, ...)
{
	char *p=(char *) &format;
	int i;
	struct person per;

	/*(1)将abcd传入format,为了取得后面的数据123,定义一个指针指向format,通过指针移动读取*/
	printf("arg1: %s\n",format);//abcd传入了第一个format
	p=p+sizeof(char *);
	i=*((int *)p);
	printf("arg2:%d\n",i);


	/*传入结构体输出*/
	p=p+sizeof(int);
	per=*((struct person *)p);
	printf("arg3: .name = %s, .age = %d, .socre=%c  .id=%d\n",\
		         per.name,   per.age,   per.score, per.id); 
	return 0;
}

int main(int argc,char **argv)
{
	
	struct  person per={"www.100ask.org",10,'A',123};//
	
	printf("sizeof(char   )=%d\n",sizeof(char   ));
	printf("sizeof(int    )=%d\n",sizeof(int    ));
	printf("sizeof(char  *)=%d\n",sizeof(char  *));
	printf("sizeof(char **)=%d\n",sizeof(char **));	
	printf("sizeof(struct  person)=%d\n",sizeof(struct  person));	
	
	//push_test("abcd",123); //(1)
	push_test("abcd",123,per);//(2)
	return 0;
}	

传入push_test可变参数结构体时,内存中的表示如下:

输出:

sizeof(char   )=1
sizeof(int    )=4
sizeof(char  *)=4
sizeof(char **)=4
sizeof(struct  person)=16
arg1: abcd
arg2:123
arg3: .name = www.100ask.org, .age = 10, .socre=A  .id=123

由于在x86(32位机器)平台下,GCC编译器默认按4字节对齐,    
如:结构体4字节对齐,即结构体成员变量所在的内存地址是4的整数倍。

字节对齐

可以通过使用gcc中的__attribute__选项来设置指定的对齐大小。

1):
__attribute__ ((packed)),让所作用的结构体取消在编译过程中的优化对齐,按照实际占用字节数进行对齐。

2):
__attribute((aligned (n))),让所作用的结构体成员对齐在n字节边界上。
 如果结构体中有成员变量的字节长度大于n,则按照最大成员变量的字节长度来对齐。


 
#include <stdio.h>
/*
由于在x86(32位机器)平台下,GCC编译器默认按4字节对齐,	
如:结构体4字节对齐,即结构体成员变量所在的内存地址是4的整数倍。

可以通过使用gcc中的__attribute__选项来设置指定的对齐大小。

1):
__attribute__ ((packed)),让所作用的结构体取消在编译过程中的优化对齐,
按照实际占用字节数进行对齐。

2):
__attribute((aligned (n))),让所作用的结构体成员对齐在n字节边界上。
 如果结构体中有成员变量的字节长度大于n,
 则按照最大成员变量的字节长度来对齐。

*/
struct  person{
	char *name;
	int  age;
	char score;
	int  id;
};

struct  person1{
	char *name;
	int  age;
	char score;
	int  id;
}__attribute__ ((packed));

struct  person2{
	char *name;
	int  age;
	char score;
	int  id;
}__attribute((aligned (4)));

int main(int argc,char **argv)
{
	struct  person per={"www.100ask.org",10,'A',123};
	printf("sizeof(char   )=%d\n",sizeof(char   ));
	printf("sizeof(int    )=%d\n",sizeof(int    ));
	printf("sizeof(char  *)=%d\n",sizeof(char  *));
	printf("sizeof(char **)=%d\n",sizeof(char **));	
	printf("sizeof(struct  person)=%d\n",sizeof(struct  person));
	printf("sizeof(struct  person1)=%d\n",sizeof(struct  person1));
	printf("sizeof(struct  person2)=%d\n",sizeof(struct  person2));	

	printf("&per.name  =%p,per.name  =%s\n",&per.name ,per.name);
	printf("&per.age   =%p,per.age   =%d\n",&per.age  ,per.age);	
	printf("&per.score =%p,per.score =%d\n",&per.score,per.score);		
	printf("&per.id    =%p,per.id    =%d\n",&per.id   ,per.id);			
	return 0;
}

输出:

sizeof(char   )=1
sizeof(int    )=4
sizeof(char  *)=4
sizeof(char **)=4
sizeof(struct  person)=16
sizeof(struct  person1)=13
sizeof(struct  person2)=16
&per.name  =0xff8cbc9c,per.name  =www.100ask.org
&per.age   =0xff8cbca0,per.age   =10
&per.score =0xff8cbca4,per.score =65
&per.id    =0xff8cbca8,per.id    =123

默认情况下的内存分布

自动确定可变参数

变参函数的使用:

将上述程序改为变参函数:

#include <stdio.h>
#include <stdarg.h>
struct person{
	char *name;
	int age;
	char score;
	int id;
};
//int printf(const char *format, ...);
/*
	* 打印出传入参数,format为第一个传入参数,...为可变参数
*/
int push_test(const char *format, ...)
{
	//char *p=(char *) &format;
	int i;
	struct person per;
	va_list p;
	
	/*(1)将abcd传入format,为了取得后面的数据123,定义一个指针指向format,通过指针移动读取*/
	printf("arg1: %s\n",format);//abcd传入了第一个format
	//p=p+sizeof(char *);
	va_start(p, format );//移动指针p到第一个变参变量

	//==============
	/*指针对连续空间操作时: 1) 取值  2)移动指针*/
	//i=*((int *)p);
	//p=p+sizeof(int);
	i = va_arg(p,int);//在已知int的情况下,返回p所指向的变参变量给i,并且移动指针p到下一个变量
	printf("arg2:%d\n",i);

	//==============			 
	/*指针对连续空间操作时: 1) 取值 2)指针赋值空,避免野指针*/ 
	/*传入结构体输出*/	
	//per=*((struct person *)p);
	per = va_arg(p,struct person);
	va_end( p );
	printf("arg3: .name = %s, .age = %d, .socre=%c  .id=%d\n",\
		         per.name,   per.age,   per.score, per.id); 
	return 0;
}

int main(int argc,char **argv)
{
	
	struct  person per={"www.100ask.org",10,'A',123};
	
	printf("sizeof(char   )=%d\n",sizeof(char   ));
	printf("sizeof(int    )=%d\n",sizeof(int    ));
	printf("sizeof(char  *)=%d\n",sizeof(char  *));
	printf("sizeof(char **)=%d\n",sizeof(char **));	
	printf("sizeof(struct  person)=%d\n",sizeof(struct  person));	
	
	//push_test("abcd",123); //(1)
	push_test("abcd",123,per);//(2)
	return 0;
}	

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值