D14. UART实验-ARM体系结构与接口技术-嵌入式学习LV9

DAY14. UART实验


如果出现图片无法查看可能是网络问题,我用的GitHub+图床保存的图片,可以参考我另外一篇文章GitHub的使用方法含网络问题解决
GitHub使用教程含网络问题_github加速器_肉丸子QAQ的博客-CSDN博客


相关作业和资料已上传,请在主页自行查看

1. UART帧格式详解

UART Universal Asynchronous Receiver Transmitter

通用异步收发器,是一种通用的串行异步通信总线 该总线有两条数据线,可以实现全双工的发送和接收 在嵌入式系统中常用于主机与辅助设备之间的通信

并行通信

串行通信

单工通信

半双工

全双工

波特率

波特率用于描述UART通信时的通信速度,其单位为 bps(bit per second)即每秒钟传送的bit的数量(不是字节)

UART帧格式

  • 规定:空闲状态数据线上状态是高电平

  • 发送前要发送一个信号告诉对方要开始发送了,所以起始位低位

  • 先发高位再发低位

  • 奇偶校验,不能修正错位

  • 串口只能发一个字节,多字节不能连续发送,发完一个需要停止后再发

  • 如何区分发送的是01还是0011

  • 通过波特率来区分,双发都要掐表计算(比如发送一个1是1秒)
  • 掐表是双方各自掐表,会出现双方掐表的时间不一样(比如一个秒一个0.1秒),解决办法就是要停止掐表,即只能一个一个发

UART硬件连接

交叉连接
D14. UART实验-ARM体系结构与接口技术-嵌入式学习LV9_第1张图片

UART控制器

一般情况下处理器中都会集成UART控制器 我们使用UART进行通信时候只需对其内部的相关寄存器进行设置即可

2. Exynos4412的UART控制器

  • 只用了两个口,其他线没接
  • 为了增强抗干扰能力增加了一个芯片(TTL信号转232信号)(不理解就当上面两条线是直连的)
  • 可以看到一个引脚很多功能,使用串口的时候需要选择相应的功能
    image-20230807100906593

引脚功能设置

设置引脚功能的实质是让引脚在芯片内部连接到某一个对应的控制器上

  • 4412拥有5个串口
  • 工作原理
    • 发送
  • 接收

3. UART寄存器详解

串口相关寄存器

(此节只用到部分寄存器)

  • 设置引脚功能
	/*1.将GPA1_0和GPA1_1设置成UART2的接收和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);
  • ULCONn

本次实验数据为8位,停止位1位,不校验,正常(有线)模式

	/*2.设置UART2的帧格式 8位数据位 1位停止位 无校验 正常模式 ULCON2[6:0]*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
  • UCONn

本次实验只用了部分位

image-20230807205155136
D14. UART实验-ARM体系结构与接口技术-嵌入式学习LV9_第2张图片

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

四种工作模式

  • 轮寻:cpu不断主动查看FIFO是否有数据来
  • 中断:串口控制器去通知CPU
  • DMA:直接将接收的数据放到内存中不经过cpu
  • UTRSTATn
/*等待发送寄存器为空,即上一个数据已经发送完成 UTRSTAT2[1]*/
while(!(UART2.UTRSTAT2 & (1 << 1)));
  • UTXHn和URXHn
		//将要发送的数据写入发射寄存器 UTXH2就能发送过去
		UART2.UTXH2 = 'A'; //SecureCRT默认只能显示字符不能显示数字
  • UBRDIVnc

D14. UART实验-ARM体系结构与接口技术-嵌入式学习LV9_第3张图片

  • UFRACVALn

image-20230807211543907

如何设置波特率

image-20230807211706445

	/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;
}

4. UART编程

  1. 发送数据
#include "exynos_4412.h"
int main()
{
	/*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); //7位清零 0111 1111 = ox7f

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/

	UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
	UART2.UBRDIV2 = 53;  //(100000000 / (115200 x 16))-1 = 53.25
	UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4

	while(1)
		{
			//将要发送的数据写入发射寄存器 UTXH2就能发送过去
			UART2.UTXH2 = 'A'; //SecureCRT默认只能显示字符不能显示数字
        	//UART2.UTXH2 = 'B'; //SecureCRT默认只能显示字符不能显示数字
        	//UART2.UTXH2 = 'C'; //SecureCRT默认只能显示字符不能显示数字
        	//UART2.UTXH2 = 'D'; //SecureCRT默认只能显示字符不能显示数字
		}

	return 0;
}

现象:终端循环发送

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0a9bqVUQ-1691459920316)(https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308080953427.png)]

将注释去掉再看现象:没有规律,和发送的数据不一样

  • cpu写入TXH速度比发送速率115200快很多,发送一次cpu写入可能上万次,所以当TXH发完一个后再去获取CPU发来的数据是不确定哪个的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nvFY8aoX-1691459920316)(https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308080953429.png)]

解决办法:通过读取状态寄存器的值来判断是否写入THX中

#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); //7位清零 0111 1111 = ox7f

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/

	UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
	UART2.UBRDIV2 = 53;  //(100000000 / (115200 x 16))-1 = 53.25
	UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
    
}

void UART_Send_Byte(char Dat)
{
    /*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
			while(!(UART2.UTRSTAT2 & (1 << 1))); //这个条件表示这个寄存器第一位的状态
			{
			//将要发送的数据写入发射寄存器 UTXH2就能发送过去
			UART2.UTXH2 = Dat; //SecureCRT默认只能显示字符不能显示数字
			}
}
char UART_Rec_Byte(void)
{
	char Dat = 0; 
    /*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
    if(UART2.UTRSTAT2 & 1) // 0位不需要移动
    {
        /*从接收寄存器中读取接收到的数据 URXH2*/
        Dat = UART2.URXH2;   
        return Dat;
    }
    else 
        return 0;
}

int main()
{
    UART_Init();
    char RecDat = 0;
	while(1)
		{
         	RecDat  = UART_Rec_Byte();
			if(RecDat == 0)
            {   
                
            }
        else
        {
            RecDat  = RecDat+1;
            UART_Send_Byte(RecDat);
            
        }
		}

	return 0;
}

现象:在键盘输入a会打印b,因为程序写着接收的数据加一

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOkn5C2b-1691459920317)(https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308080953430.png)]

5. 输入输出重定向

  • 发送字符串
#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); //7位清零 0111 1111 = ox7f

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/

	UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
	UART2.UBRDIV2 = 53;  //(100000000 / (115200 x 16))-1 = 53.25
	UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
    
}

void UART_Send_Byte(char Dat)
{
    /*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
			while(!(UART2.UTRSTAT2 & (1 << 1))); //这个条件表示这个寄存器第一位的状态
			{
			//将要发送的数据写入发射寄存器 UTXH2就能发送过去
			UART2.UTXH2 = Dat; //SecureCRT默认只能显示字符不能显示数字
			}
}
char UART_Rec_Byte(void)
{
	char Dat = 0; 
    /*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
    if(UART2.UTRSTAT2 & 1) // 0位不需要移动
    {
        /*从接收寄存器中读取接收到的数据 URXH2*/
        Dat = UART2.URXH2;   
        return Dat;
    }
    else 
        return 0;
}

//
//发送字符串
void UART_Send_Str(char * pstr)
{
	while(*pstr != '\0')
		UART_Send_Byte(*pstr++);
}

int main()
{
    UART_Init();
    char RecDat = 0;
	while(1)
		{
			UART_Send_Str("Hello World\n");

		}c

	return 0;
}
  • 可以使用printf函数,已经在资料里面提供不用自己写
#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); //7位清零 0111 1111 = ox7f

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/

	UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
	UART2.UBRDIV2 = 53;  //(100000000 / (115200 x 16))-1 = 53.25
	UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
    
}

void UART_Send_Byte(char Dat)
{
    /*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
			while(!(UART2.UTRSTAT2 & (1 << 1))); //这个条件表示这个寄存器第一位的状态
			{
			//将要发送的数据写入发射寄存器 UTXH2就能发送过去
			UART2.UTXH2 = Dat; //SecureCRT默认只能显示字符不能显示数字
			}
}
char UART_Rec_Byte(void)
{
	char Dat = 0; 
    /*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
    if(UART2.UTRSTAT2 & 1) // 0位不需要移动
    {
        /*从接收寄存器中读取接收到的数据 URXH2*/
        Dat = UART2.URXH2;   
        return Dat;
    }
    else 
        return 0;
}

//
//发送字符串
void UART_Send_Str(char * pstr)
{
	while(*pstr != '\0')
		UART_Send_Byte(*pstr++);
}

int main()
{
    UART_Init();
    char RecDat = 0;
	while(1)
		{
			printf("你好");	

		}

	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EbTEFev3-1691459920318)(https://cdn.jsdelivr.net/gh/ybm2002329/mdlmage/202308080953431.png)]

输入输出重定向

自己写的printf和c库的输出定向不同

  • c库是定向到显卡
  • 自己写的是定向到串口

所以有区别的


6. 作业

1.若使用UART协议发送一个字节的数据0x63,画出信号线上的时序图

注:8位数据位、无校验位、一位停止位

2.编程实现电脑远程控制LED状态

注:在终端上输入‘2’,LED2点亮,再次输入‘2’,LED2熄灭… …

#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); //7位清零 0111 1111 = ox7f

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/

	UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
	UART2.UBRDIV2 = 53;  //(100000000 / (115200 x 16))-1 = 53.25
	UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
    
}

void UART_Send_Byte(char Dat)
{
    /*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
			while(!(UART2.UTRSTAT2 & (1 << 1))); //这个条件表示这个寄存器第一位的状态
			{
			//将要发送的数据写入发射寄存器 UTXH2就能发送过去
			UART2.UTXH2 = Dat; //SecureCRT默认只能显示字符不能显示数字
			}
}
char UART_Rec_Byte(void)
{
	char Dat = 0; 
    /*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
    if(UART2.UTRSTAT2 & 1) // 0位不需要移动
    {
        /*从接收寄存器中读取接收到的数据 URXH2*/
        Dat = UART2.URXH2;   
        return Dat;
    }
    else 
        return 0;
}

void LED_Init(void)
{
	GPX2.CON = GPX2.CON & (~( 0xF << 28)) | (0X1 << 28);

}

void LED_ON(void)
{
	GPX2.DAT = GPX2.DAT | (1 << 7);
}

void LED_OFF(void)
{
	GPX2.DAT = GPX2.DAT & (~(1 << 7));
}

//
//发送字符串
void UART_Send_Str(char * pstr)
{
	while(*pstr != '\0')
		UART_Send_Byte(*pstr++);
}

int main()
{
    UART_Init();
	LED_Init();
	LED_OFF();
    char RecDat = 0;
	int a = 0;
	while(1)
		{
			RecDat = UART_Rec_Byte();
			if(RecDat == '2' && a==0)
			{UART_Send_Byte(RecDat);
				LED_ON();
				a = 1;
			}
			else if(RecDat == '2' && a ==1 )
			{UART_Send_Byte(RecDat);
				LED_OFF();
				a = 0;
			}
			else{
			UART_Send_Byte(RecDat);

			}
		}

	return 0;
}

你可能感兴趣的:(arm开发,学习,linux,c语言)