目录
一、定时器和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的格式为奇数或者偶数时对应的奇偶校验位为1;
- 为保证下一个字节发送前的起始位能够表现出来,校验位之后发送一个停止位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): 标志数据帧结束。