27、PIC18F4525的UART通信与中断应用详解

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 复位控制寄存器

列表

  1. UART将微处理器内部的并行数据转换为外部的串行数据,反之亦然。
  2. UART通信是异步的,不需要时钟信号。
  3. 可以使用“握手”方式,但会占用更多的I/O引脚和电缆。
  4. 中断可以使PIC从程序的任何位置跳转到ISR。
  5. 要使用中断,需要启用全局中断和特定的外设中断。

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等方法计算并验证数据

列表

  1. 程序运行时,终端输入字符会触发中断,PIC进行相应处理。
  2. 示例程序存在未考虑数据冲突和缺乏数据验证的局限性。
  3. 不同寄存器的设置对UART通信和中断功能有重要影响。
  4. 中断处理具有实时响应和提高效率的优势,适用于数据通信和传感器数据采集等场景。
  5. 未来开发可结合更多外设模块,优化程序,探索不同通信协议。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值