PIC18F4525的UART通信与中断应用详解
1. 代码优化与I2C协议回顾
在编程中,代码的优化可以提高效率和简洁性。例如,原本的两行代码:
disData = readFromEEPROM(adhigh,adlow);
rxData [b] = disData;
可以合并为一行:
rxData[b] = readFromEEPROM(adhigh,adlow);
这样不仅功能相同,还节省了一行代码。
此前,我们学习了PIC如何使用I2C协议与外部设备进行通信,包括一个主设备与两个从设备的通信方式。还了解了24LC256 EEPROM如何用于永久存储外部数据,以及如何对其进行读写操作。同时,也掌握了如何使用TC74监测温度并通过PIC进行控制。
2. UART概述
UART(Universal Asynchronous Receive and Transmit)即通用异步收发传输器,是大多数与外界通信的微处理器中使用的电路。微处理器内部采用并行总线系统进行通信,这种方式比串行通信快得多。早期的并行总线只有4位宽,如今的总线宽度可达64位,即使使用相同的时钟,通信速度也能提高16倍。
然而,与外部世界通信时,使用64根线的电缆成本过高。早期打印机使用的8位或16位宽的带状电缆,长度也限制在3米左右。因此,为了降低成本,外部通信通常采用仅使用两根线的串行方式。
UART的作用就是将微处理器内部的并行数据转换为外部的串行数据,同时将外部的串行数据转换为内部的并行数据。它包含PISO(并行输入串行输出)移位寄存器和SIPO(串行输入并行输出)移位寄存器。
与SPI模块不同,UART通信是异步的,不需要时钟信号,只需要连接PIC的发送(TX)和接收(RX)引脚的两根线即可。当然,也可以使用“握手”方式,即在设备之间添加RTS(准备传输)和CTS(清除发送)信号,但这会占用更多的I/O引脚和电缆。
3. UART使用示例程序
为了展示如何使用UART,下面是一个示例程序,它实现了以下功能:
1. LCD显示消息:“Coded Uart”。
2. 主程序使LED以1秒为间隔闪烁。
3. 连接到PIC的终端,当在终端输入字符时,该字符将通过UART发送到PIC。
4. PIC接收到字符后,将触发中断,并在LCD上显示该字符。
5. PIC将数字1到9发送到终端,并在终端上显示。
6. 每个字符显示在终端上时,PIC还会发送换行指令,使每个新字符显示在新的一行。
以下是该程序的代码:
/*This is a basic program to control the LCD and the UART
using the PIC 18F4525
Written by H H Ward dated 02/04/16.
modified 24/06/16 to include running from an interrupt
whilst main program is simply flashing an led
the ISR sends out the character received at the uart back
to a terminal and then sends the numbers 1 to 9
inserting a line after each number has been sent to the
terminal.
The program also echos the character received onto the LCD
connected to the pic.
using the LCD connected to PORTB
*/
#include <xc.h>
#include <conFigInternalOscNoWDTNoLVP.h>
#include <4bitLCDPortb.h>
//some variables
unsigned char n, count, newdatain = 0;
//the subroutines
void shortdelay ()
{
TMR0 = 0;
while (TMR0 < 255);
}
void delay (unsigned char t)
{
for (n = 0; n < t; n ++)
{
TMR0 = 0;
while (TMR0 < 255);
}
}
void interrupt isr1 ()
{
if (PIR1bits.RCIF == 1)
{
PIR1bits.RCIF = 0;
newdatain = RCREG;
lcdData = newdatain;
lcdOut ();
TXREG = newdatain;
shortdelay ();
TXREG = 0x0a;
shortdelay ();
for (n = 0x30; n < 0x3A; n++)
{
TXREG = 0X0A;
shortdelay ();
TXREG = 0x0D;
shortdelay ();
TXREG = n;
shortdelay ();
TXREG = 0x0A;
}
}
}
void main ()
{
PORTA = 0;
PORTB = 0;
PORTC = 0;
PORTD = 0;
TRISA = 0X01;
TRISB = 0x00;
TRISC = 0b10000000;
TRISD = 0x00;
ADCON0 = 0x00;
ADCON1 = 0x0F;
OSCTUNE = 0b10000000;
OSCCON = 0b01110000;
T0CON = 0b11000111;
INTCON = 0b11000000;
PIE1bits.RC1IE = 1;
TXSTA = 0b00100000;
RCSTA = 0b10010000;
BAUDCON = 0b00000000;
SPBRG = 12;
setUpTheLCD ();
clearTheScreen ();
writeString ("Coded Uart");
line2 ();
while (1)
{
PORTDbits.RD1 = PORTDbits.RD1^1;
delay (20);
}
}
4. 中断及其工作原理
在深入分析程序之前,有必要了解一下中断的概念。中断会使PIC执行一个特殊的子程序,即ISR(Interrupt Service Routine,中断服务程序)。这个子程序包含了PIC在返回主程序之前必须执行的一系列指令。
PIC在执行每条指令时,都会检查是否有强制其运行ISR的中断发生。在“取指和执行”周期中,即使没有中断被触发,PIC也会检查一个特殊的“中断标志”位。这个中断标志可以由多种事件设置,其中之一就是UART从外部源接收数据。
对于PIC18f4525,控制中断使用了十个寄存器,分别是:
1. INTOCON:中断控制寄存器
2. INTCON2:中断控制寄存器2
3. INTCON3:中断控制寄存器3
4. PIR1:外设中断请求控制寄存器1
5. PIR2:外设中断请求控制寄存器2
6. PIE1:外设中断使能控制寄存器1
7. PIE2:外设中断使能控制寄存器2
8. IPR1:中断优先级请求控制寄存器1
9. IPR2:中断优先级请求控制寄存器2
10. ROCN:复位控制寄存器
中断分为全局中断和外设中断两类:
- 全局中断:涵盖所有外部和内部中断。
- 外设中断:来自PIC的所有外设模块,如定时器、捕获比较和PWM(CCP 1和2模块)、UART 1和模块、I2C模块、MSSP模块等。
要使用任何中断,首先要通过将INTCON寄存器的第7位(GIE位)设置为逻辑‘1’来启用全局中断。然后,要启用任何外设中断,还需要将INTCON寄存器的第6位(PIE位)设置为逻辑‘1’。在示例程序中,这一步是通过以下代码实现的:
INTCON = 0b11000000;
此外,要使用特定的外设中断,还需要设置该中断的使能位。在示例程序中,启用UART中断的代码如下:
PIE1bits.RC1IE = 1;
当UART接收到数据时,PIC会自动将PIR1bits.RCIF位设置为逻辑‘1’。但要确保这个动作能产生中断,相应的外设使能位必须事先设置。当RCIF位变为逻辑‘1’时,中断标志会变为逻辑‘1’,PIC会立即停止当前正在执行的任务,跳转到ISR执行其中的指令。
与普通子程序不同,中断可以使PIC从程序的任何位置跳转到ISR,因为中断标志在每次取指时都会被检查,这样PIC就不会错过已启用的中断。
5. 示例程序分析
以下是对示例程序中关键指令的分析:
-
短延迟函数(Lines 16 - 19)
:
void shortdelay ()
{
TMR0 = 0;
while (TMR0 < 255);
}
这个函数用于创建大约33ms的短延迟,在向终端发送数据之间需要这样的小延迟。
- 可变延迟函数(Lines 20 - 27) :
void delay (unsigned char t)
{
for (n = 0; n < t; n ++)
{
TMR0 = 0;
while (TMR0 < 255);
}
}
这个函数用于创建可变的延迟,在主程序的第80行使用。
- 中断服务程序(Lines 28 - 51) :
void interrupt isr1 ()
{
if (PIR1bits.RCIF == 1)
{
PIR1bits.RCIF = 0;
newdatain = RCREG;
lcdData = newdatain;
lcdOut ();
TXREG = newdatain;
shortdelay ();
TXREG = 0x0a;
shortdelay ();
for (n = 0x30; n < 0x3A; n++)
{
TXREG = 0X0A;
shortdelay ();
TXREG = 0x0D;
shortdelay ();
TXREG = n;
shortdelay ();
TXREG = 0x0A;
}
}
}
- `if (PIR1bits.RCIF == 1)`:检查UART接收器是否触发了中断。
- `PIR1bits.RCIF = 0`:清除UART1接收相关的中断标志,以防止在准备好之前再次触发中断。
- `newdatain = RCREG`:将从终端接收到的数据加载到变量`newdatain`中。
- `lcdData = newdatain`:将数据复制到`lcdData`变量中,准备发送到LCD。
- `lcdOut ()`:调用`lcdOut`子程序,将从终端接收到的数据显示在LCD上。
- `TXREG = newdatain`:将接收到的数据加载到UART的发送寄存器`TXREG`中,开始将数据发送到终端。
- `TXREG = 0x0a`:将换行符的ASCII码加载到`TXREG`中,使终端光标移动到下一行。
- `for (n = 0x30; n < 0x3A; n++)`:循环发送数字0到9到终端,并在每个数字后添加换行符。
- 主程序(Lines 52 - 82) :
void main ()
{
PORTA = 0;
PORTB = 0;
PORTC = 0;
PORTD = 0;
TRISA = 0X01;
TRISB = 0x00;
TRISC = 0b10000000;
TRISD = 0x00;
ADCON0 = 0x00;
ADCON1 = 0x0F;
OSCTUNE = 0b10000000;
OSCCON = 0b01110000;
T0CON = 0b11000111;
INTCON = 0b11000000;
PIE1bits.RC1IE = 1;
TXSTA = 0b00100000;
RCSTA = 0b10010000;
BAUDCON = 0b00000000;
SPBRG = 12;
setUpTheLCD ();
clearTheScreen ();
writeString ("Coded Uart");
line2 ();
while (1)
{
PORTDbits.RD1 = PORTDbits.RD1^1;
delay (20);
}
}
- `INTCON = 0b11000000`:启用全局和外设中断。
- `PIE1bits.RC1IE = 1`:启用UART产生中断的功能。
- `TXSTA = 0b00100000`:设置TXSTA(发送状态)寄存器的第5位,启用UART发送数据的功能。
- `RCSTA = 0b10010000`:设置RCSTA(接收状态)寄存器的第7位和第5位,启用串行端口并设置RX和TX引脚的正确方向,同时启用UART的接收功能。
- `BAUDCON = 0b00000000`:清除BAUDCON(波特率控制)寄存器的所有位,设置UART为8位波特率发生器,只使用SPBRG寄存器,忽略SPBRGH寄存器。
- `SPBRG = 12`:将波特控制值的低字节SPBRG加载为12,以产生9600的波特率。
这个示例程序展示了如何使用PIC18F4525通过UART与带有键盘的终端(如PC)进行通信。但该程序假设用户在发送信息时会小心操作,没有考虑可能发生的冲突,也没有对发送和接收的数据进行验证。主程序只是简单地使LED以0.5秒为间隔闪烁,当终端向PIC的UART发送字符时,会触发中断,PIC会中断主程序,执行ISR中的指令。
总结
通过本文的学习,我们了解了UART的基本概念和工作原理,以及如何在PIC18F4525中使用UART进行通信。同时,也掌握了中断的概念和使用方法,以及如何编写和分析相关的程序。希望这些知识能帮助你在实际项目中更好地应用UART和中断技术。
流程图
graph TD;
A[开始] --> B[初始化PIC端口和寄存器];
B --> C[初始化LCD并显示消息];
C --> D[主程序:LED闪烁];
D --> E{终端输入字符?};
E -- 是 --> F[触发中断];
F --> G[清除中断标志];
G --> H[读取接收到的数据];
H --> I[在LCD上显示数据];
I --> J[将数据发送回终端];
J --> K[发送换行符];
K --> L[循环发送数字0 - 9到终端];
L --> D;
E -- 否 --> D;
表格
| 寄存器 | 作用 |
|---|---|
| INTOCON | 中断控制寄存器 |
| INTCON2 | 中断控制寄存器2 |
| INTCON3 | 中断控制寄存器3 |
| PIR1 | 外设中断请求控制寄存器1 |
| PIR2 | 外设中断请求控制寄存器2 |
| PIE1 | 外设中断使能控制寄存器1 |
| PIE2 | 外设中断使能控制寄存器2 |
| IPR1 | 中断优先级请求控制寄存器1 |
| IPR2 | 中断优先级请求控制寄存器2 |
| ROCN | 复位控制寄存器 |
列表
- UART将微处理器内部的并行数据转换为外部的串行数据,反之亦然。
- UART通信是异步的,不需要时钟信号。
- 可以使用“握手”方式,但会占用更多的I/O引脚和电缆。
- 中断可以使PIC从程序的任何位置跳转到ISR。
- 要使用中断,需要启用全局中断和特定的外设中断。
PIC18F4525的UART通信与中断应用详解
6. 程序运行结果展示
当程序运行时,会有特定的显示效果。若在连接到PIC的终端(如运行Tera Term软件的笔记本电脑)上输入字符,程序会有相应反馈。
假设在笔记本电脑上输入字符“ H ”,并且关闭了Tera Term软件的自动回显功能。此时,PIC会将“ H ”回显到终端上显示,之后PIC会把数字0到9依次发送到终端,每个数字显示在新的一行,效果如图11 - 2所示。同时,连接到PIC的LCD上也会有对应显示,如图11 - 3所示。
7. 程序的局限性与改进思考
此示例程序虽然展示了PIC18F4525通过UART与终端通信的基本功能,但存在一些局限性。
-
未考虑数据冲突
:程序假设用户在发送信息时会小心操作,未考虑可能发生的数据冲突情况。例如,当多个设备同时向PIC发送数据时,可能会出现数据碰撞,导致数据丢失或错误。
-
缺乏数据验证
:程序没有对发送和接收的数据进行验证,无法确保发送和接收的数据一致。在实际应用中,数据的准确性至关重要,缺乏验证机制可能会导致系统出现错误。
针对这些局限性,可以进行以下改进:
-
添加冲突检测机制
:可以在程序中添加逻辑来检测数据冲突。例如,在UART接收数据时,检查是否有其他设备正在发送数据,若有则等待合适的时机再接收数据。
-
增加数据验证
:可以使用校验和、循环冗余校验(CRC)等方法对数据进行验证。在发送数据时,计算数据的校验值并一起发送;在接收数据时,重新计算校验值并与接收到的校验值进行比较,若不一致则要求重新发送数据。
8. 不同寄存器设置的影响分析
在示例程序中,多个寄存器的设置对UART通信和中断功能起着关键作用,下面详细分析不同寄存器设置的影响。
| 寄存器 | 设置值 | 作用 |
| ---- | ---- | ---- |
| TXSTA | 0b00100000 | 设置位5,启用UART发送数据的功能 |
| RCSTA | 0b10010000 | 设置位7和位5,启用串行端口,设置RX和TX引脚方向,启用UART接收功能 |
| BAUDCON | 0b00000000 | 清除所有位,设置UART为8位波特率发生器,只使用SPBRG寄存器,忽略SPBRGH寄存器 |
| SPBRG | 12 | 加载波特控制值的低字节,产生9600的波特率 |
- TXSTA寄存器 :位5的设置决定了UART是否能够发送数据。若该位未设置,UART将无法发送数据到终端。
- RCSTA寄存器 :位7的设置启用了串行端口,并确定了RX和TX引脚的方向;位5的设置启用了UART的接收功能。若这两位未正确设置,UART将无法正常接收数据。
- BAUDCON寄存器 :其设置影响了波特率发生器的工作模式。设置为0b00000000时,UART使用8位波特率发生器,只依赖SPBRG寄存器。
- SPBRG寄存器 :其值决定了波特率的大小。不同的值会产生不同的波特率,在示例中设置为12以产生9600的波特率。
9. 中断处理的优势与应用场景
中断处理在PIC编程中有诸多优势,并且适用于多种应用场景。
-
优势
-
实时响应
:PIC可以在任何时候响应中断,无需等待主程序执行到特定位置。例如,当UART接收到数据时,能立即触发中断,执行相应的处理程序,保证数据的及时处理。
-
提高效率
:在主程序执行其他任务时,中断可以同时处理外部事件,避免了主程序不断轮询外部设备状态的开销,提高了系统的整体效率。
-
应用场景
-
数据通信
:在UART通信中,当接收到数据时触发中断,及时处理数据,避免数据丢失。
-
传感器数据采集
:当传感器检测到特定事件(如温度变化、光线变化等)时,触发中断,读取传感器数据并进行相应处理。
10. 总结与展望
通过对UART通信和中断处理的学习,我们掌握了PIC18F4525通过UART与终端通信的方法,以及如何使用中断来处理外部事件。这些知识在嵌入式系统开发中具有重要的应用价值。
在未来的开发中,可以进一步扩展这些知识。例如,结合更多的外设模块,实现更复杂的功能;优化程序,提高系统的稳定性和可靠性;探索不同的通信协议,实现更高效的数据传输。
流程图
graph TD;
A[开始改进程序] --> B[添加冲突检测机制];
B --> C[增加数据验证功能];
C --> D[测试改进后的程序];
D --> E{是否通过测试?};
E -- 是 --> F[部署到实际项目];
E -- 否 --> B;
表格
| 改进方向 | 具体措施 |
|---|---|
| 冲突检测 | 在UART接收数据时,检查其他设备是否正在发送数据 |
| 数据验证 | 使用校验和、CRC等方法计算并验证数据 |
列表
- 程序运行时,终端输入字符会触发中断,PIC进行相应处理。
- 示例程序存在未考虑数据冲突和缺乏数据验证的局限性。
- 不同寄存器的设置对UART通信和中断功能有重要影响。
- 中断处理具有实时响应和提高效率的优势,适用于数据通信和传感器数据采集等场景。
- 未来开发可结合更多外设模块,优化程序,探索不同通信协议。
超级会员免费看
1087

被折叠的 条评论
为什么被折叠?



