IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> PIC24HJ单片机的UART -> 正文阅读

[嵌入式]PIC24HJ单片机的UART

UART的发送与接收(RS232)

  1. 单片机通过串口发送数据时,需要选择停止位、校验位和数据位。停止位有三种选择:1、1.5、2;校验位也有三种选择:奇校验、偶校验和无校验。数据位有四种选择:8bit,7bit,6bit,5bit。一般没有特殊要求,数据位选择8bit,刚好一字节,方便数据处理。这三个信息一般配置为:1个停止位,无校验位和8位数据。传输一帧数据为9个bit。
  2. PIC24HJ128GP506A Uart的相关寄存器:a.Uartx模式寄存器、b.Uartx状态和控制寄存器、c、Uartx发送寄存器、d、Uartx接收寄存器(只读)、Uartx波特率发送寄存器。波特率设置跟PIC24HJ所用的时钟有关,有具体的波特率计算公式,下面列出。
  3. 先说明PIC24HJ的波特率如何计算:BRGH是Uartx模式寄存器中的一位,BRGH = 0表示使用低速波特率;BRGH = 1表示使用高速波特率。高速和低速波特率计算方法稍有区别。a.BRGH = 0:在这里插入图片描述
    主要根据公式1反推出公式2,假设我们要设置波特率为9600,就可以反推出UxBRG的值,UxBRG是波特率发生器寄存器,公式2计算值就是写入该寄存器。b.BRGH = 1:在这里插入图片描述
    可以看到区别就在于16变成了4,自然波特率就比刚才的大了。将反推出来的值赋给UxBRG寄存器即可,设置好相应的波特率。
    设置UxMODE寄存器,这个寄存器一般设置停止位,数据位和校验位,自动波特率的开启和选择波特率的模式(高速或低速),该寄存器控制着UART的开启。寄存器的每一位请自行查看PIC24的数据手册;接下来就是设置UxSTA寄存器,该寄存器设置发送和接收时产生中断的方式,一般选择发送或接收一个char产生中断。然后该寄存器控制着发送功能的开启。这里有必要说明:必选先开启UART功能,然后再开启TX发送功能,在程序里面的顺序很重要,否则无法使用UART的发送功能。。UART数据接收与发送寄存器很简单,一个用来读数据,另一个用来写数据。下面给出UART的配置代码,并使用XCOM上位机通信。
// PIC24HJ128GP506A Configuration Bit Settings

// 'C' source line config statements

// FBS
#pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
#pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)
#pragma config RBS = NO_RAM             // Boot Segment RAM Protection (No Boot RAM)

// FSS
#pragma config SWRP = WRPROTECT_OFF     // Secure Segment Program Write Protect (Secure segment may be written)
#pragma config SSS = NO_FLASH           // Secure Segment Program Flash Code Protection (No Secure Segment)
#pragma config RSS = NO_RAM             // Secure Segment Data RAM Protection (No Secure RAM)

// FGS
#pragma config GWRP = OFF               // General Code Segment Write Protect (User program memory is not write-protected)
#pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)

// FOSCSEL
#pragma config FNOSC = PRIPLL           // Oscillator Mode (Primary Oscillator (XT, HS, EC) w/ PLL)
#pragma config IESO = ON                // Two-speed Oscillator Start-Up Enable (Start up with FRC, then switch)

// FOSC
#pragma config POSCMD = HS              // Primary Oscillator Source (HS Oscillator Mode)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function (OSC2 pin has clock out function)
#pragma config FCKSM = CSECMD           // Clock Switching and Monitor (Clock switching is enabled, Fail-Safe Clock Monitor is disabled)

// FWDT
#pragma config WDTPOST = PS32768        // Watchdog Timer Postscaler (1:32,768)
#pragma config WDTPRE = PR128           // WDT Prescaler (1:128)
#pragma config WINDIS = ON              // Watchdog Timer Window (Watchdog Timer in Window mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)

// FPOR
#pragma config FPWRT = PWR128           // POR Timer Value (128ms)

// FICD
#pragma config ICS = PGD1               // Comm Channel Select (Communicate on PGC1/EMUC1 and PGD1/EMUD1)
#pragma config JTAGEN = ON              // JTAG Port Enable (JTAG is Enabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#define FCY         40000000
#define BAUDRATE    9600
#define BRGVAL      ((FCY/BAUDRATE)/16)-1
unsigned int i;
char RecvData;


void uart_init(void)
{
    //Configure System Clock.
    /* Fosc = Fin * M /(N1 * N2) */
    PLLFBD = 18;                        //M = 20
    CLKDIVbits.PLLPOST = 0;             //N1 = 2
    CLKDIVbits.PLLPRE  = 0;             //N2 = 2
    OSCTUN = 0;
    RCONbits.SWDTEN = 0;
    //Wait for PLL to lock
    while(OSCCONbits.LOCK != 1);
    
    U1MODEbits.STSEL = 0;
    U1MODEbits.PDSEL = 0;
    U1MODEbits.ABAUD = 0;
    U1MODEbits.BRGH  = 0;
    
    
    U1BRG = BRGVAL;                          //波特率9600
    
    
        U1STAbits.UTXISEL0 = 0;
    U1STAbits.URXISEL = 0;
    
    IEC0bits.U1TXIE = 1;
    IEC0bits.U1RXIE = 1;
    IFS0bits.U1TXIF = 0;
    IPC2bits.U1RXIP = 0x02;
    IPC3bits.U1TXIP = 0x01;
    
    U1MODEbits.UARTEN = 1;                    //使能UART
    U1STAbits.UTXEN   = 1;                    //使能UART 发送
    
    
    for(i = 0; i < 4160; i++){
        Nop();
    }
    U1TXREG = 'a';
}

void led_init(void)
{
    TRISDbits.TRISD10 = 0;                    //设置D7端口为输出模式
}


int main(void)
{
    
    uart_init();
    led_init();                              //初始化LED
    
    while(1){
        if(U1STAbits.URXDA == 1){
            RecvData = U1RXREG;
            U1TXREG = RecvData;
        }
        if(RecvData == 'o'){
            LATDbits.LATD10 = 0;
        }else if(RecvData == 'c'){
            LATDbits.LATD10 = 1;
        }
    }
    
    return 0;
    
}

void __attribute__((__interrupt__, __no_auto_psv__)) _U1TXInterrupt(void)
{
    
    IFS0bits.U1TXIF = 0;
}


void __attribute__((__interrupt__, __no_auto_psv__)) _U1RXInterrupt(void)
{

    IFS0bits.U1RXIF = 0;
}

代码中宏定义 #define FCY 40000000以为指令执行速度,和上面给出公式中的Fcy是一个概念,表示当前UART使用的时钟源是40MHz,这是我设置外部时钟源得到的时钟频率,有关时钟频率的设置在我上一篇时钟设置里面有提到。在这里提醒一下自己:写完代码的时候,发现上位机下发数据,单片机接收数据变量的值,并不与上位机的值一样,找了一天代码的错误,代码怎么看都没错,最后发现硬件连接上接了一个RS485转RS232的器件,它就是罪魁祸首,有时候检查错误,错误不一定在软件上,每个地方都应该排查,不能忽视。这个程序是基于硬件RS232实现的全双工串口通信。接下来在此基础上介绍RS485半双工的串口通信。

UART实现RS485通信

  1. 其实了解RS232和RS485通信的区别后,RS485很容易实现。它是半双工通信,意思就是说:在同一时段内,只能有一个发送方和接收方,不能同时发送和接收。RS485区分逻辑1和逻辑是计算两根信号的电压差值来判决逻辑1和逻辑0的。术语叫做差分信号,就是说RS485传输的是差分信号。使用TC485H芯片实现485通信。该芯片有一个控制传输方向的引脚D/R,这样就可以使用PIC24HJ一个普通I/O口来控制数据传输方向了。
  2. 这里需要注意:单片机发送数据到上位机,这时候D/R引脚默认为这个传输方向,而上位机不能下发数据到单片机,所以想要把数据传输控制权交给上位机,就需要想办法把TC485H的D/R引脚电平进行跳变。否则RS485通信就成了单工通信了。
  3. 这里给出一个粗略的解决办法:开启一个定时器,让单片机发送完数据,到时间后,跳板TC485H D/R引脚电平,将发送权交给上位机。上位机可以发送特定的数据再将发送权交给单片机。这样就实现了RS485半双工通讯。下面给出参考代码:
// PIC24HJ128GP506A Configuration Bit Settings

// 'C' source line config statements

// FBS
#pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
#pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)
#pragma config RBS = NO_RAM             // Boot Segment RAM Protection (No Boot RAM)

// FSS
#pragma config SWRP = WRPROTECT_OFF     // Secure Segment Program Write Protect (Secure segment may be written)
#pragma config SSS = NO_FLASH           // Secure Segment Program Flash Code Protection (No Secure Segment)
#pragma config RSS = NO_RAM             // Secure Segment Data RAM Protection (No Secure RAM)

// FGS
#pragma config GWRP = OFF               // General Code Segment Write Protect (User program memory is not write-protected)
#pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)

// FOSCSEL
#pragma config FNOSC = PRIPLL           // Oscillator Mode (Primary Oscillator (XT, HS, EC) w/ PLL)
#pragma config IESO = ON                // Two-speed Oscillator Start-Up Enable (Start up with FRC, then switch)

// FOSC
#pragma config POSCMD = HS              // Primary Oscillator Source (HS Oscillator Mode)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function (OSC2 pin has clock out function)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor (Both Clock Switching and Fail-Safe Clock Monitor are disabled)

// FWDT
#pragma config WDTPOST = PS32768        // Watchdog Timer Postscaler (1:32,768)
#pragma config WDTPRE = PR128           // WDT Prescaler (1:128)
#pragma config WINDIS = OFF             // Watchdog Timer Window (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = ON              // Watchdog Timer Enable (Watchdog timer always enabled)

// FPOR
#pragma config FPWRT = PWR128           // POR Timer Value (128ms)

// FICD
#pragma config ICS = PGD1               // Comm Channel Select (Communicate on PGC1/EMUC1 and PGD1/EMUD1)
#pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG is Disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#define FCY             40000000
#define BAUDRATE        9600
#define BRGVALUE        ((FCY/BAUDRATE)/16)-1

void init_clk(void)
{
    /* initial Fcy to 40MHz */
    PLLFBD = 18;                     //M = 18
    CLKDIVbits.PLLPOST = 0;          //N1 = 2
    CLKDIVbits.PLLPRE  = 0;          //N2 = 2
    OSCTUN = 0;                      
    RCONbits.SWDTEN = 0;             //关闭看门狗时钟
    while(OSCCONbits.LOCK != 1);     //Wait for PLL to Lock.
}

void timer1_init(void)
{
    T1CONbits.TON = 0;
    T1CONbits.TCS = 0;                //select internal clock 40MHz
    T1CONbits.TGATE = 0;
    T1CONbits.TCKPS = 0;              //一分频
    TMR1 = 0x00;
    PR1  = 39999;                     //定时1ms
    
    /* Timer1 Interrupt */
    IPC0bits.T1IP = 0x01;
    IFS0bits.T1IF = 0;
}



void relay_init(void)
{
    TRISDbits.TRISD11 = 0;
}

void init_uart_rs485(void)
{
    uint16_t i;
    /* initial tc485h D/R io */
    TRISFbits.TRISF6 = 0;             //配置为输出状态
    LATFbits.LATF6 = 0;               //默认为接收,上位机先发送
    
    U1MODEbits.STSEL = 0;             //0 stop-bit
    U1MODEbits.PDSEL = 0;             //No Parity and 8 Data-Bits
    U1MODEbits.ABAUD = 0;             //auto baud close
    U1MODEbits.BRGH  = 0;             //low baud mode
    
    U1BRG = BRGVALUE;                 //set baudrate to 9600 bps
    
    U1STAbits.URXISEL = 0;            //Recv one character to interrupt.
    U1STAbits.UTXISEL0 = 0;           //Trans one character to interrupt.
    
    /* Configure TX & RX interrupt */
    IPC3bits.U1TXIP = 0x02;
    IPC2bits.U1RXIP = 0x03;
    IFS0bits.U1TXIF = 0;
    IFS0bits.U1RXIF = 0;
    IEC0bits.U1TXIE = 1;
    IEC0bits.U1RXIE = 1;
    
    U1MODEbits.UARTEN = 1;              //Enable UART
    U1STAbits.UTXEN   = 1;              //Enable TX
    
    for(i = 0; i < 4160; i++){
        Nop();
    }
}


int main(void) 
{
    char Recv;
    
    init_clk();
    init_uart_rs485();
    timer1_init();
    relay_init();
    while(1){
        if(U1STAbits.URXDA == 1){
            Recv = U1RXREG;
        }
        if(Recv == 'o'){
            LATDbits.LATD11 = 1;
            
        }
        if(Recv == 't'){
            
            LATFbits.LATF6 = 1;         //rs485 TX
       
                                 //定时1ms
            T1CONbits.TON = 1;
            IEC0bits.T1IE = 1;

        }
        if(Recv == 'c'){

            LATDbits.LATD11 = 0;
        }
        
    }
    
    
    return 0;
}


void __attribute__((__interrupt__, __no_auto_psv__)) _U1TXInterrupt(void)
{
    //Clear TX IF 
    IFS0bits.U1TXIF = 0;
}

void __attribute__((__interrupt__, __no_auto_psv__)) _U1RXInterrupt(void)
{
    
    //Clear RX IF
    IFS0bits.U1RXIF = 0;
}

void __attribute__((__interrupt__, __no_auto_psv__)) _T1Interrupt(void)
{
    static uint16_t cnt = 0;
    cnt++;
    if(cnt == 500)
        U1TXREG = 'a';
    if(cnt == 1000){
        LATFbits.LATF6 = 0;                 //rs485 RX
    
        //关闭定时器
        T1CONbits.TON = 0;
        cnt = 0;
    }
    
    
    //Clear T1 UP_Flow IF
    IFS0bits.T1IF = 0;
}

在定时器1中断函数中一定要注意把计数器cnt的值在恰当位置清零,刚开始我没有清零,上位机下发’t’给单片机后,第一次单片机很快给上位机回复了字符a,进行第二次这样的操作,单片机回复字符a需要很长时间,大概一分钟左右,造成这个结果的原因就是cnt没有清零,cnt的数据类型是无符号整型,它的最大值为216-1,**单片机是一毫秒进一次定时器中断,**也就是说,在单片机第一次发送完字符a后,cnt的值一直朝着最大值自增,这里计算一下从零加到最大值需要多少时间,自增一次的时间是1ms,216 - 1是65535ms,折合65.5s,一分钟多一点,这还是自增到最大值,还需要加上cnt溢出后,从零开始计数到1000的时间,即1s,所以说第二次发送字符a的时间间隔为65.5s+1s=66.5秒,时间还是有点稍长。这里再次提醒自己:使用计数器一定要清零。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:23:46  更:2021-08-09 10:24:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 8:41:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码