嵌入式硬件学习(五)——定时器和UART

一、定时器和PWM定时器

1、什么是定时器

   定时器顾名思义是为了准确获得一个时间而取名,S3C2440A 有 5 个 16 位定时器,其中定时器 0、1、2 和 3 具有脉宽调制(PWM)功能。定时器 4 是一个无输出引脚的内部定时器。定时器 0 还包含用于大电流驱动的死区发生器。S3C2440中有三个时钟分别是FCLK、HCLK和PCLK,分别工作频率是400MHz、100MHz和50MHz,PWM定时器就挂载在PCLK下,如下图所示。
  本次任务为使用定时器4产生一个1s的时间,工作过程为:首先由PCLK时钟频率为50MHz,然后用8位预分频器进行25倍分频为2MHz,然后通过分频器再进行2分频得到最后1MHz信号,周期为1us。然后有TCNTB计数器,配置为1000,该计数器每次就会从1000开始以1us进行递减,递减到0之后会电平翻转,这样就会得到1ms的时间,1000个1ms得到1s。
在这里插入图片描述

2、相关寄存器

(1)定时器配制寄存器 0(TCFG0) :配置预分频值,25分频,按照要求写入为24。
在这里插入图片描述
(2)定时器配制寄存器 1(TCFG1) :配置分频值,2分频。
在这里插入图片描述
(3)定时器控制寄存器 1(TCON) :在这块可以配置是否自动重载,若选择自动重载递减寄存器减到0之后重新回到TCNTB;选择更新TCNTB4,写入之后写1更新再置0;还有启动定时器功能。
在这里插入图片描述
(4)定时器 4 计数缓冲寄存器(TCNTB4) :设置定时器 4 的计数缓冲器的值,本次设为1000,之后如果打开定时器,会采用递减的形式减到0,表示1ms。
在这里插入图片描述
(5)定时器 4 计数监视寄存器(TCNTO4) :读取该寄存器可以看到现在的递减计数器到了多少。
在这里插入图片描述

3、如何配置一个定时器

(1)通过定时器中断

   按照下面的代码,首先TCNTB4 装入1000,计数器减到0之后会触发一次中断,中断函数中定义了静态变量n,加static修改的是该函数的生存期,由动态生存期改为静态生存期,函数结束后空间不销毁,每次都会++n,当n到1000之后,led翻转一次,为1s。

void init_delay(void)
{
	TCFG0 |= (24 << 8);			// 预分频为25倍,50M分为2M		
	TCFG1 &= ~(0x0F << 16);		// 分频为2倍,2M分为1M

	TCNTB4 = 1000; 				// 初始化
	TCON |= (1 << 22);			// 自动重载模式
	TCON |= (1 << 21);	  		// 手动更新
	TCON &= ~(1 << 21);
	
	enable_irq(IRQ_TIMER4);	

	TCON |= (1 << 20);	   		 // 启动定时器

	register_iqr(IRQ_TIMER4, timer4_handler);
}
void timer4_handler(void)
{
	static unsigned int n = 0;
	++n;
	if(n > 1000)
	{
		 ledAllNor();
		 n = 0;
	}
}

(2)非中断模式

   上述方法代码简单,但会遇到一个问题:每次都要使用不同的时间,这样每次都需要修改init_delay函数中的TCNTB4,使用不变,如何可以通过一个函数传参的操作完成该项功能。
在这里插入图片描述

  该方法使用不产生中断,监视定时器,具体思想为:首先该定时器是16为定时器,最大值为0xFFFF,让这个定时器一直递减,递减到0之后向下溢出返回0xFFFF,然后如果要延时一段时间,假如为num,该延时函数首先使用定时器计数监视寄存器(TCNTO4)看看现在是几(old_counter) ,然后死循环一直去看现在的值是多少(new_counter),因为是递减计数器,所以new_counter要小于old_counter,同时采用一个counter变量去累加(old_counter-new_counter)。等到counter >= num后跳出循环,会得到一个精确延时。如果遇到溢出,那么new_counter会大于old_counter,那么counter += old_counter + 0xffff - new_counter。

void udelay(unsigned short num)
{
	unsigned int counter, old_counter, new_counter;
	counter = 0;
	old_counter = TCNTO4;			// 将旧cnt赋值
	while(1)
	{
		new_counter = TCNTO4;	  	// 把new_cnt赋值
		if(new_counter != old_counter)
		{
			if(new_counter < old_counter)
			{
				counter += old_counter - new_counter;
			}
			else
			{
				counter += old_counter + 0xffff - new_counter;
			}
			old_counter = new_counter;
			if(counter >= num)
			{
				break;
			}
		}	
	}
}
void mdelay(unsigned int num)
{
	while(num--)
	{
		udelay(1000);
	}
}

4、配置PWM定时器

  定时器 0 比较缓冲寄存器(TCMPB0) :相比定时器多了一个比较寄存器,设置定时器 0 的比较缓冲器的值,用来设置占空比。
在这里插入图片描述

三、UART

1、串口通信基本概念

  嵌入式系统中的通信是指两个或两个以上的主机之间的数据互交,这里的主机可以是计算机也可以是嵌入式主机,甚至可以是芯片。主机间通信的方式一般可以分为两类:并行通信和串行通信。
(1)并行通信是指多个比特同时通过并行线进行传输,这种方式的传输速率较高,但会占用大量的芯片资源;
(2)串行通信是指将数据拆分成一个个比特,按照先后次序在一根总线上进行发送,串行通信有着系统占用资源少,结构简单等优点,是主机间通信的常用方式。根据工作方式可以分为:单工、半双工和全双工模式。

2、UART介绍

  串口通信(Serial Port)是串行通信的一种,属于串行通信中的异步通信。我们经常听到的RS232、RS485、RS422都是串行通信。作为常用的串行通信方式,以TTL为例,串口通信在不同主机之间的数据格式为:
在这里插入图片描述

  1. 空闲时数据线为高电平;
  2. 发送发发送一个低电平表示起始位;
  3. 发送的第一个比特是最低为(最右边);
  4. 校验位分为奇校验,偶校验和无校验。奇偶校验时数据位1的格式为奇数或者偶数时对应的奇偶校验位为1;
  5. 为保证下一个字节发送前的起始位能够表现出来,校验位之后发送一个停止位1。

  S3C2440A 的通用异步收发器(UART)配有 3 个独立异步串行 I/O(SIO)端口,每个都可以是基于中断或基
于 DMA 模式的操作。每个 UART 包含一个波特率发生器、发送器、接收器和一个控制单元,如下图所示。本次使用的是UART0。
在这里插入图片描述

3、相关寄存器配置

  本次实验使用UART0实现数据收发,使用非FIFO模式,采用非中断发送,中断接收的形式。

(1)线路控制寄存器0(ULCON0) :配置发送数据长度、奇偶校验模式和停止位数。
在这里插入图片描述
(2)控制寄存器0(UCON0) :配置时钟、中断选择类型、发送和接收选用的模式。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(3)状态寄存器0(UTRSTAT0) :判断发送寄存器和接受寄存器中值是否为空。
在这里插入图片描述
(4)发送缓冲寄存器0(UTXH0) :发送寄存器中写入需要发送的值。
在这里插入图片描述
(5)接收缓冲寄存器0(URXH0) :接收寄存器中读出需要接收的值。

在这里插入图片描述

(6)波特率配制寄存器 0(UBRDIV0) :配置为9600,得到的值为325。
在这里插入图片描述
在这里插入图片描述

4、相关代码

char buffer[128];  				// 接收返回的数据
unsigned int pos;				// 判断返回了多少字节

void uart0_handler(void)		// 接收中段服务函数
{
	if(SUBSRCPND & (1 <<0))		//子中断如果是UART0中的接收中断
	{
		buffer[pos++] = URXH0;	// 将接受到的值写入buffer中
	}
	SUBSRCPND = SUBSRCPND;
}

void init_uart0(void)
{
	unsigned int t; 
	GPHCON &= ~(0x0f << 4);	 	// 配置GPIO端口为uart0
	GPHCON |= (10 << 4);
   	
	t =	ULCON0;
	t &= ~(0x1f << 2);			// 普通模式、无奇偶校验、每帧一个停止位
	t |= (3 << 0);				// 每帧发送8bits
	ULCON0 = t;

	t =  UCON0;
	t &= ~((3 << 10)|(3 << 8)|(0x0f << 4));		// PCLK和Rx接收到数据发出中断请求
	t &= (0x0f << 0);
	t |= (5 << 0);				// 发送模式和接收模式设为中断或者轮训	
	UCON0 = t;
	UBRDIV0 = 325; 				// 波特率设置为9600	
	
	enable_irq(INT_UART0);
	INTSUBMSK  &= ~(1 << 0);	// uart0接收次中断源设置为取消屏蔽	
	register_irq(INT_UART0, uart0_handler);
		
	pos = 0;	
}

void uart0_send_char(unsigned char ch) 		// 发送单个字节
{
	UTXH0 = ch;
	while((UTRSTAT0 & (1 << 2)) == 0);	  	// 这个字节发完没
}

void send_buffer(const char *p, unsigned int len)	 // 发送多个字节
{
	unsigned int i;
	for(i = 0; i < len; ++i)
	{
		uart0_send_char(*p++);	
	}
}

5、Modbus通信协议

在这里插入图片描述
Modbus 数据帧由以下几个部分组成:

  • 起始码(Start Bit): 标志数据帧的开始。在串行通信中通常是静默的时间间隔。
  • 地址码(Address Field): 用于指定目标设备的唯一地址。
  • 功能码(Function Code): 定义请求的操作类型。
  • 数据区(Data Field): 包含具体的操作参数或返回的数据信息,例如寄存器地址、数据值等。
  • 校验码(CRC/LRC): 用于检测传输数据是否存在错误,保证通信的可靠性。
  • 结束码(Stop Bit): 标志数据帧结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值