PIC24F开发板无法通过UART收发数据

Cannot send or receive data through UART on PIC24F development board

我正在尝试使用 MPLAB X 将简单的 UART 测试程序上传到 PIC24F Curiosity Board 并使用 TeraTerm 验证数据传输。该评估板使用 PIC24FJ128GA204 微控制器。我已确保按照程序中的定义在 TeraTerm 上设置串口配置。

对于数据传输,我使用 USB 到 RS-232 UART 电缆连接到我的桌面。我仔细检查了我的电缆连接,它们与程序中定义的一样。我没有在电缆上使用 +5 Vcc,我已将电缆接地,但我已尝试连接和断开接地。

目前不会传输任何数据,虽然我注意到当我对电路板进行编程时,有时 TeraTerm 会显示一些随机值,我不确定这是否意味着存在能够传输数据的连接或者它是只是随机噪声。

目前我的UART初始化如下:

#include <xc.h>
#define U_ENABLE                 0x8008                   // enable UART, BREGH=1, 1 stop, 8 data bits, no parity, RTS/CTS flow control
#define U_TX                     0x0400                  // enable transmission, clear all flags

#define _XTAL_FREQ               8000000 //set to internal oscillator frequency

//** UART2 RS232 asynchronous communication demonstration
void initialize_UART(void) {

    U1BRG =  104;                                                  // initialize the baud rate generator to 104, an equivalent value of 9524
    U1MODE = U_ENABLE;                                             // initialize the UART module  
    U1STA = U_TX;                                                  // enable the receiver
    
    //****CONFIGURING FOR UART****
    //MICROCONTROLLER PIN || UART WIRE
    //RP0/RB0 = U1RX
    //RP1/RB1 = U1CTS (Clear to send)
    //RP2/RB2 = U1TX
    //RP3/RB3 = UR1RTS ()
    
    // Unlock Registers (per MCU data sheet)
    asm volatile ("MOV #OSCCON, w1 \n"
     "MOV #0x46, w2 \n"
     "MOV #0x57, w3 \n"
     "MOV.b w2, [w1] \n"
     "MOV.b w3, [w1] \n"
     "BCLR OSCCON, #6") ;
 
 // Configure Input Functions (Table 11-3 in Datasheet)
    // Assign U1RX To Pin RP0
    //RPINR
    RPINR18bits.U1RXR = 0;
    
    // Assign U1CTS To Pin RP1
    RPINR18bits.U1CTSR = 1;

// Configure Output Functions (Table 11-4 in Datasheet)
    // Assign U1TX To Pin RP2
    RPOR1bits.RP2R = 3;
    
    // Assign U1RTS To Pin RP3
    RPOR1bits.RP3R = 4;
    
    // Lock Registers
    asm volatile ("MOV #OSCCON, w1 \n"
    "MOV #0x46, w2 \n"
    "MOV #0x57, w3 \n"
    "MOV.b w2, [w1] \n"
    "MOV.b w3, [w1] \n"
    "BSET OSCCON, #6") ; 

}

我的UART发送数据函数:

char UART_send_char(char a) 
{
    while (PORTBbits.RB1 == 1); //wait for clear to send
    while (U1STAbits.UTXBF); //wait while Tx buffer is full
    U1TXREG = a;
    return a;
}

我如何测试 main() 中的函数:

int main(void) 
{
    TRISA = 0;
    
    // init the UART1 serial port
    initialize_UART();

     // main loop
     while (1)
    {
        PORTAbits.RA10 = 1; //for debugging purposes
        UART_send_char('>');
    }
}

该程序应不断向 TeraTerm 控制台发送字符“>”,但我却什么也没收到。 RA10 连接到一个 LED,它会亮起,所以我知道程序已进入主循环,但没有数据正在发送。我将配置位设置为以 8 MHz 运行的内部振荡器,并禁用了 PLL。 BREGH 设置为 1,因此我使用了第 247 页 datasheet 中给出的波特率发生器的相应公式,Fcy 值为 8 MHz/2.

我是 MCU 编程的新手,花了几个小时试图解决这个问题但无济于事,因此非常感谢任何帮助。

这是一个完整的单文件应用程序,它使用 MikroElektronical USB UART click 在 DM240004 PIC24F Curiosity 开发板上运行。

/*
 * https://github.com/dsoze1138/24FJ128GA204_DM240004
 *
 * File:   main.c
 * Author: dan1138
 * Target: PIC24FJ128GA204
 * Compiler: XC16 v1.60
 * IDE: MPLABX v5.45
 *
 * Created on November 24, 2020, 12:02 PM
 *
 *                                                           PIC24FJ128GA204
 *               +----------------+               +--------------+                +-----------+                +--------------+
 * J4_MOSI <>  1 : RB9/RP9        :    LED2 <> 12 : RA10         :   J4_SDA <> 23 : RB2/RP2   : X2-32KHZ <> 34 : RA4/SOSCO    :
 *  RGB_G  <>  2 : RC6/RP22       :         <> 13 : RA7          :   J4_SCL <> 24 : RB3/RP3   :     LED1 <> 35 : RA9          :
 *  RGB_B  <>  3 : RC7/RP23       :         <> 14 : RB14/RP14    :      POT <> 25 : RC0/RP16  :    J4_CS <> 36 : RC3/RP19     :
 *     S2  <>  4 : RC8/RP24       :         <> 15 : RB15/RP15    :          <> 26 : RC1/RP17  :   J4_SCK <> 37 : RC4/RP20     :
 *     S1  <>  5 : RC9/RP25       :     GND -> 16 : AVSS         :          <> 27 : RC2/RP18  :    RGB_R <> 38 : RC5/RP21     :
 *    3v3  <>  6 : VBAT           :     3v3 -> 17 : AVDD         :      3v3 -> 28 : VDD       :      GND -> 39 : VSS          :
 *   10uF  ->  7 : VCAP           : ICD_VPP -> 18 : MCLR         :      GND -> 29 : VSS       :      3v3 -> 40 : VDD          :
 *         <>  8 : RB10/RP11/PGD2 :   J4_AN <> 19 : RA0/AN0      :          <> 30 : RA2/OSCI  :    J4_RX <> 41 : RB5/RP5/PGD3 :
 *         <>  9 : RB11/RP11/PGC2 :  J4_RST <> 20 : RA1/AN1      :          <> 31 : RA3/OSCO  :    J4_TX <> 42 : RB6/RP6/PGC3 :
 *         <> 10 : RB12/RP12      : ICD_PGD <> 21 : RB0/RP0/PGD1 :          <> 32 : RA8       :          <> 43 : RB7/RP7      :
 *         <> 11 : RB13/RP13      : ICD_PGC <> 22 : RB1/RP1/PGC1 : X2-32KHZ <> 33 : RB4/SOSCI :  J4_MISO <> 44 : RB8/RP8      :
 *               +----------------+               +--------------+                +-----------+                +--------------+
 *                                                              TQFP-44
 * 
 * Description:
 * 
 * Bare metal initialization of the DM240004 Curiosity Board
 * 
 * Setup the system oscillator for 32MHz using the internal FRC and the 4x PLL.
 * Turn on the 32.768KHz secondary oscillator.
 * Use UART1 and printf to send a message as 9600 baud.
 * Flash LED2 on for 500 milliseconds then off for 500 milliseconds.
 * 
 */
// CONFIG4
#pragma config DSWDTPS = DSWDTPS1F      // Deep Sleep Watchdog Timer Postscale Select bits (1:68719476736 (25.7 Days))
#pragma config DSWDTOSC = LPRC          // DSWDT Reference Clock Select (DSWDT uses LPRC as reference clock)
#pragma config DSBOREN = OFF            // Deep Sleep BOR Enable bit (DSBOR Disabled)
#pragma config DSWDTEN = OFF            // Deep Sleep Watchdog Timer Enable (DSWDT Disabled)
#pragma config DSSWEN = OFF             // DSEN Bit Enable (Deep Sleep operation is always disabled)
#pragma config PLLDIV = PLL4X           // USB 96 MHz PLL Prescaler Select bits (4x PLL selected)
#pragma config I2C1SEL = DISABLE        // Alternate I2C1 enable bit (I2C1 uses SCL1 and SDA1 pins)
#pragma config IOL1WAY = OFF            // PPS IOLOCK Set Only Once Enable bit (The IOLOCK bit can be set and cleared using the unlock sequence)

// CONFIG3
#pragma config WPFP = WPFP127           // Write Protection Flash Page Segment Boundary (Page 127 (0x1FC00))
#pragma config SOSCSEL = ON             // SOSC Selection bits (SOSC circuit selected)
#pragma config WDTWIN = PS25_0          // Window Mode Watchdog Timer Window Width Select (Watch Dog Timer Window Width is 25 percent)
#pragma config PLLSS = PLL_FRC          // PLL Secondary Selection Configuration bit (PLL is fed by the on-chip Fast RC (FRC) oscillator)
#pragma config BOREN = OFF              // Brown-out Reset Enable (Brown-out Reset Disabled)
#pragma config WPDIS = WPDIS            // Segment Write Protection Disable (Disabled)
#pragma config WPCFG = WPCFGDIS         // Write Protect Configuration Page Select (Disabled)
#pragma config WPEND = WPENDMEM         // Segment Write Protection End Page Select (Write Protect from WPFP to the last page of memory)

// CONFIG2
#pragma config POSCMD = NONE            // Primary Oscillator Select (Primary Oscillator Disabled)
#pragma config WDTCLK = LPRC            // WDT Clock Source Select bits (WDT uses LPRC)
#pragma config OSCIOFCN = ON            // OSCO Pin Configuration (OSCO/CLKO/RA3 functions as port I/O (RA3))
#pragma config FCKSM = CSECMD           // Clock Switching and Fail-Safe Clock Monitor Configuration bits (Clock switching is enabled, Fail-Safe Clock Monitor is disabled)
#pragma config FNOSC = FRC              // Initial Oscillator Select (Fast RC Oscillator (FRC))
#pragma config ALTCMPI = CxINC_RB       // Alternate Comparator Input bit (C1INC is on RB13, C2INC is on RB9 and C3INC is on RA0)
#pragma config WDTCMX = WDTCLK          // WDT Clock Source Select bits (WDT clock source is determined by the WDTCLK Configuration bits)
#pragma config IESO = OFF               // Internal External Switchover (Disabled)

// CONFIG1
#pragma config WDTPS = PS32768          // Watchdog Timer Postscaler Select (1:32,768)
#pragma config FWPSA = PR128            // WDT Prescaler Ratio Select (1:128)
#pragma config WINDIS = OFF             // Windowed WDT Disable (Standard Watchdog Timer)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT disabled in hardware; SWDTEN bit disabled)
#pragma config ICS = PGx1               // Emulator Pin Placement Select bits (Emulator functions are shared with PGEC1/PGED1)
#pragma config LPCFG = OFF              // Low power regulator control (Disabled - regardless of RETEN)
#pragma config GWRP = OFF               // General Segment Write Protect (Write to program memory allowed)
#pragma config GCP = OFF                // General Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF             // JTAG Port Enable (Disabled)
/*
 * Defines for system oscillator frequency.
 * Make sure that the PIC initialization selects this frequency.
 */
#define FSYS (32000000ul)
#define FCY  (FSYS/2ul)
/*
 * Target specific Special Function Register definitions
 */
#include <xc.h>
/*
 * Standard header files
 */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/*
 * Initialize this PIC
 */
void PIC_Init(void)
{
    uint16_t ClockSwitchTimeout;

    /* 
     * Disable all interrupt sources 
     */ 
    __builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */ 
    IEC0 = 0; 
    IEC1 = 0; 
    IEC2 = 0; 
    IEC3 = 0; 
    IEC4 = 0; 
    IEC5 = 0; 
    IEC6 = 0; 
    IEC7 = 0; 
    __builtin_disi(0x0000); /* enable interrupts */ 
    
    INTCON1bits.NSTDIS = 1; /* Disable interrupt nesting */
    
    /*
     * At Power On Reset the configuration words set the system clock
     * to use the FRC oscillator. At this point we need to enable the
     * PLL to get the system clock running at 32MHz.
     * 
     * Clock switching on the 24FJ family with the PLL can be a bit tricky.
     * 
     * First we need to check if the configuration words enabled clock
     * switching at all, then turn off the PLL, then setup the PLL and
     * finally enable it. Sounds simple, I know. Make sure you verify this 
     * clock setup on the real hardware.
     */

    if(!OSCCONbits.CLKLOCK) /* if primary oscillator switching is unlocked */
    {
        /* Select primary oscillator as FRC */
        __builtin_write_OSCCONH(0b000);

        /* Request switch primary to new selection */
        __builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));

        /* wait, with timeout, for clock switch to complete */
        for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);

        CLKDIV   = 0x0000; /* set for FRC clock 8MHZ operations */

        /* Select primary oscillator as FRCPLL */
        __builtin_write_OSCCONH(0b001);
        /* Request switch primary to new selection */
        __builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
        
        /* ALERT: This may be required only when the 96MHz PLL is used */
        CLKDIVbits.PLLEN = 1;

        /* wait, with timeout, for clock switch to complete */
        for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);

        /* wait, with timeout, for the PLL to lock */
        for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && !OSCCONbits.LOCK;);
        
        /* at this point the system oscillator should be 32MHz */
    }
    
    /* Turn on Secondary Oscillation Amplifier */
    __builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_SOSCEN_POSITION));
    
    /* Turn off all analog inputs */
    ANSA = 0;
    ANSB = 0;
    ANSC = 0;
}
/*
 * WARNING: Not a portable function.
 *          Maximum delay 16384 instruction cycles.
 *          At 16 MIPS this is 1024 microseconds.
 *
 *          Minimum  1MHz instruction cycle clock.
 */
void delay_us(unsigned short delay)
{
    if (delay > (uint16_t)(16383.0 / (FCY/1E6)))
    {
        asm("   repeat  #16383\n"
            "   clrwdt        \n"
            ::);

    }
    else
    {
        asm("   repeat  %0    \n"
            "   clrwdt        \n"
        :: "r" (delay*(uint16_t)(FCY/1000000ul)-1));
    }
}
/*
 * WARNING: Not a portable function.
 *          Maximum 16MHz instruction cycle clock.
 *          Minimum  8Khz instruction cycle clock.
 */
void delay_ms(unsigned long delay)
{
    if(delay)
    {
        asm("1: repeat  %0    \n"
            "   clrwdt        \n"
            "   sub  %1,#1    \n"
            "   subb %d1,#0   \n"
            "   bra  nz,1b    \n"
        :: "r" (FCY/1000ul-6ul), "C" (delay));
    }
}
/*
 * Initialize the UART
 */
void UART_Init(void)
{
/**    
     Set the UART1 module to run at 9600 baud with RB6 as TX out and RB5 as RX in.
*/
    TRISBbits.TRISB6 = 0;
    LATBbits.LATB6 = 1;
    __builtin_write_OSCCONL(OSCCON & ~_OSCCON_IOLOCK_MASK); // unlock PPS

    RPINR18bits.U1RXR = 0x0005;    //RB5->UART1:U1RX
    RPOR3bits.RP6R    = 0x0003;    //RB6->UART1:U1TX

    __builtin_write_OSCCONL(OSCCON | _OSCCON_IOLOCK_MASK); // lock PPS
    
    // STSEL 1; IREN disabled; PDSEL 8N; UARTEN enabled; RTSMD disabled; USIDL disabled; WAKE disabled; ABAUD disabled; LPBACK disabled; BRGH enabled; URXINV disabled; UEN TX_RX; 
    // Data Bits = 8; Parity = None; Stop Bits = 1;
    U1MODE = (0x8008 & ~(1<<15));  // disabling UARTEN bit
    // UTXISEL0 TX_ONE_CHAR; UTXINV disabled; URXEN disabled; OERR NO_ERROR_cleared; URXISEL RX_ONE_CHAR; UTXBRK COMPLETED; UTXEN disabled; ADDEN disabled; 
    U1STA = 0x00;
    // BaudRate = 9600; Frequency = 16000000 Hz; U1BRG 416; 
    U1BRG = 0x1A0;
    // ADMADDR 0; ADMMASK 0; 
    U1ADMD = 0x00;
    // T0PD 1 ETU; PTRCL T0; TXRPT Retransmits the error byte once; CONV Direct; SCEN disabled; 
    U1SCCON = 0x00;
    // TXRPTIF disabled; TXRPTIE disabled; WTCIF disabled; WTCIE disabled; PARIE disabled; GTCIF disabled; GTCIE disabled; RXRPTIE disabled; RXRPTIF disabled; 
    U1SCINT = 0x00;
    // GTC 0; 
    U1GTC = 0x00;
    // WTCL 0; 
    U1WTCL = 0x00;
    // WTCH 0; 
    U1WTCH = 0x00;
    
    U1MODEbits.UARTEN = 1;   // enabling UART ON bit
    U1STAbits.UTXEN = 1;
}

uint8_t UART1_Read(void)
{
    while(!(U1STAbits.URXDA == 1));

    if ((U1STAbits.OERR == 1))
    {
        U1STAbits.OERR = 0;
    }
    
    return U1RXREG;
}

void UART1_Write(uint8_t txData)
{
    while(U1STAbits.UTXBF == 1);

    U1TXREG = txData;    // Write the data byte to the USART.
}

bool UART1_IsRxReady(void)
{
    return U1STAbits.URXDA;
}

bool UART1_IsTxReady(void)
{
    return ((!U1STAbits.UTXBF) && U1STAbits.UTXEN );
}

bool UART1_IsTxDone(void)
{
    return U1STAbits.TRMT;
}

int __attribute__((__section__(".libc.write"))) write(int handle, void *buffer, unsigned int len) 
{
    unsigned int i;

    for (i = len; i; --i)
    {
        UART1_Write(*(char*)buffer++);
    }
    return(len);
}
/*
 * Main Application
 */
int main(void) 
{
    /*
     * Application initialization
     */
    PIC_Init();
    UART_Init();
    
    /* Set RA10 for output to drive LED2 */
    LATAbits.LATA10 = 0;
    TRISAbits.TRISA10 = 0;

    printf("\r\nPIC24FJ128GA204 built on " __DATE__ " at " __TIME__ " Start\r\n");
    /*
     * Application process loop
     */
    for(;;)
    {
        LATAbits.LATA10 ^= 1;
        delay_ms(500);
    }
    return 0;
}

我怀疑您的问题可能是尝试使用 RTS/CTS 信号实现硬件握手。

先尝试以低波特率从PIC向PC传输串口数据

另一件事是 RB0 和 RB1 用于编程(和调试)PIC24FJ128GA204。板载在线串行编程器可能会干扰将这些引脚用于其他功能。在调试会话中,应用程序根本无法使用它们。