Author Topic: PIC24F UART  (Read 9955 times)

0 Members and 1 Guest are viewing this topic.

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
PIC24F UART
« on: July 08, 2016, 06:13:54 pm »
Hello again,

I'm trying to use UART on my PIC24FJ32MC101, and i'm trying to just send on UART. I followed this tutorial: http://www.engscope.com/pic24-tutorial/9-2-uart-usage/ however I ran into a few issues with remapable pins:

This line presented in the tutorial RPOR1bits.RP3R = 3; I couldnt implement because my PIC doesn't have them (see page 135 of the datasheet, link below, on the table with RPOR1 there's a note stating that they aren't available on my device) I also noticed that this line that sets the input, RPINR18bits.U1RXR    =   2;   , it confuses me. RPINR18, which in my mind translates to "Remappable Pin INput Register 18", ooes each remappable pin register come with each set of peripherals?

Even then, if I can set Rx via this method: RPINR18bits.U1RXR =   2; and this sets it to remappable pin 2, however to set the output (transmit) in the tutorial they use RPOR1bits.RP3R = 3; but shouldnt the syntax be RPOR1bits.U1TXR = 3; ?

As you can tell i'm confused as to a few things, however I do have some code for everybody to criticize.   

Code: [Select]
// 'C' source line config statements
#pragma config OSCIOFNC = OFF           // CLKO Enable Configuration bit (CLKO output signal is active on the OSCO pin)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#define USE_AND_OR  /* To enable AND_OR mask setting */

#include <xc.h>
#include <libpic30.h>
#include "headers.h"
// libpic30 delays
#define __delay_ms(d) \
  { __delay32( (unsigned long) (((unsigned long long) d)*(FCY)/1000ULL)); }
#define __delay_us(d) \
  { __delay32( (unsigned long) (((unsigned long long) d)*(FCY)/1000000ULL)); }

#define FP 40000000
#define BAUDRATE 9600
#define BRGVAL ((FP/BAUDRATE)/16)-1
#define DELAY_105uS asm volatile ("REPEAT, #4201"); Nop();  // 105uS delay bits
#define FCY 7370000/2 // All dsPIC33s, a few PIC24s

unsigned int i;

int main(void)

    //Set up I/O Port
    AD1PCFGL = 0xFFFF; //set to all digital I/O
    TRISBbits.TRISB15 = 0;   // LED pin is an output
    RPINR18bits.U1RXR = 2; //UART1 receive set to RP2
   
    RPOR0bits.RP0R = 3;         // Uart Transmit ???
   
    LATBbits.LATB15=0;  // Turn LED on to show that its not broken
    initUART(23);       // See formula on datasheet
    __delay_ms(5000);
   
    while(1)
    {

            UART1PutChar('t');
            UART1PutChar('e');
            UART1PutChar('s');
            UART1PutChar('t');
            UART1PutChar(' ');
    }
    return 0;
}

/**
 * Initialize the UART module
 * @param baud baud rate as calculated by formula in data sheet
 */
void initUART(int baud)
{
    U1BRG = baud;            //9600 baud
    U1MODE = 0x8000;        // Enable module
    U1STA   = 0x8400;       // Set interrupts
    IFS0bits.U1RXIF = 0;    // Reset RX interrupt flag
}

/**
 * --- UART TRANSMIT FUNCTION ---
 * Transmits a char if the TX buffer is empty
 * @param ch char to transmit
 */
void UART1PutChar(char ch)
{
    while(U1STAbits.UTXBF)
    {
        U1TXREG = ch;
    }
}

/**
 * --- UART RECIEVE FUNCTION ---
 * While there is content in the RX buffer, copy the char and reset RX register
 * @return a char from the RX register
 */
char UART1GetChar()
{
    char buffer = ' ';           // Initialize variable
    while(IFS0bits.U1RXIF == 0)
    {
        buffer = U1RXREG;
        IFS0bits.U1RXIF = 0;
    }
    return buffer;
}

I do hope me making multiple posts isn't a issue - I just want to learn, I don't mean to come off as a help vampire

Thank you everybody for the help, I do enjoy programming for the PIC microcontroller  ;D
EDIT: Silly me i forgot to say how my code is broken. It does compile yes, however when running there is no activity on the remappable pins. On some remappable pins theres a square wave, but nothing else.
Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/30009997e.pdf#G11.1048847
« Last Edit: July 08, 2016, 06:16:09 pm by netwinder »
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3474
  • Country: it
Re: PIC24F UART
« Reply #1 on: July 08, 2016, 06:28:52 pm »
you'd better read the datasheet AND the family reference manual for your pic.
on the product page on the microchip website, at the end of the page you'll find everything.
(fort the PPS, short version: RPRINxx is a register for the peripheral. the remappable peripheral input is connected to the RPyy/RPINyy pin, yy is the value you write in the RPRINxx register. this means that you can use the same pin for different peripheral inputs in case you were wondering, however for the output there is a register associated to the RPxx pin, the value in this register tell which peripheral is connected to that pin)
 

Offline 22swg

  • Frequent Contributor
  • **
  • Posts: 274
  • Country: gb
Re: PIC24F UART
« Reply #2 on: July 08, 2016, 07:00:07 pm »
Hi vampire ... One thing I did come to understand about PIC24 UART  is you have to use interrupts on the RX and read in to FIFO.  will look out my also crap code and post ... ( some one bound to disagree)
Check your tongue, your belly and your lust. Better to enjoy someone else’s madness.
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13801
  • Country: gb
    • Mike's Electric Stuff
Re: PIC24F UART
« Reply #3 on: July 08, 2016, 07:24:09 pm »
Hi vampire ... One thing I did come to understand about PIC24 UART  is you have to use interrupts on the RX and read in to FIFO. 
Not necessarily  you just need to read data out fast enough to not make the fifo overrun. Interrupts are often the easiest/best way to achieve this but not always necessary. 
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5324
  • Country: gb
Re: PIC24F UART
« Reply #4 on: July 08, 2016, 08:40:31 pm »
I've cut it down to a bare minimum to avoid too much confusion from irrelevant stuff!

1) To allow changes to the PPS you need to explicitly allow this either in runtime with a special unlock sequence, or just by turning off the IOL1WAY config bit. You can look at the PPS registers in the debugger to make sure they're changed.

2) The RP pins you chose either don't exist on your device or were used by one of the debug/programmer pins (RP0 is PGED1, RP2 doesn't exist). I switched them to RP14 (RXD) and RP13 (TXD).

Code: [Select]
#pragma config OSCIOFNC = OFF           // Primary Oscillator Output Function (OSC2 pin has clock out function)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
#pragma config IOL1WAY = OFF            // IOLOCK Protection (Allow Multiple Re-configurations)

#include <xc.h>

#define FCY 7370000/2 // All dsPIC33s, a few PIC24s
#include <libpic30.h>

#define BAUDRATE 9600
#define BRGVAL ((FCY/BAUDRATE)/16)-1

void initUART(int baud)
{
    U1BRG = baud;            //9600 baud
    U1MODEbits.UARTEN=1;
    U1STAbits.UTXEN=1; // Enable the tx
}

void UART1PutChar(char ch)
{
    while(!U1STAbits.TRMT)
    {
        Nop(); // I like multiple Nops in case the hardware debugger skids on breakpoints
        Nop();
        Nop();
    }
    U1TXREG = ch;
    LATBbits.LATB15=1; // Debug this pin with your scope
    LATBbits.LATB15=0;
}

int main(void)

    //Set up I/O Port
    AD1PCFGL = 0xFFFF; //set to all digital I/O
   
    TRISBbits.TRISB15 = 0;   // LED pin is an output
   
//    RPINR18bits.U1RXR = 2; //UART1 receive set to RP2 // There is no RP2 on this chip
    RPINR18bits.U1RXR = 14; //UART1 receive set to RP14 (not used in this example))
   
//    RPOR0bits.RP0R = 3;         // Uart Transmit ??? // RP0 is used by programming pin PGED1
    RPOR6bits.RP13R = 3;         // Uart Transmit on RP13
 
    LATBbits.LATB15=0;  // Turn LED on to show that its not broken

    initUART(BRGVAL);       // See formula on datasheet

    __delay_ms(500);
   
    while(1)
    {
            UART1PutChar('t');
            UART1PutChar('e');
            UART1PutChar('s');
            UART1PutChar('t');
            UART1PutChar(' ');
    }
    return 0;
}

 
The following users thanked this post: netwinder

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
Re: PIC24F UART
« Reply #5 on: July 08, 2016, 09:14:27 pm »
Hi vampire ... One thing I did come to understand about PIC24 UART  is you have to use interrupts on the RX and read in to FIFO. 
Not necessarily  you just need to read data out fast enough to not make the fifo overrun. Interrupts are often the easiest/best way to achieve this but not always necessary. 

Interrupts are probably the next step after this, however for now I'm trying basic implementations to understand how things tick. I'm learning trust me  ;)

I've cut it down to a bare minimum to avoid too much confusion from irrelevant stuff!

1) To allow changes to the PPS you need to explicitly allow this either in runtime with a special unlock sequence, or just by turning off the IOL1WAY config bit. You can look at the PPS registers in the debugger to make sure they're changed.

2) The RP pins you chose either don't exist on your device or were used by one of the debug/programmer pins (RP0 is PGED1, RP2 doesn't exist). I switched them to RP14 (RXD) and RP13 (TXD).

Code: [Select]
#pragma config OSCIOFNC = OFF           // Primary Oscillator Output Function (OSC2 pin has clock out function)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
#pragma config IOL1WAY = OFF            // IOLOCK Protection (Allow Multiple Re-configurations)

#include <xc.h>

#define FCY 7370000/2 // All dsPIC33s, a few PIC24s
#include <libpic30.h>

#define BAUDRATE 9600
#define BRGVAL ((FCY/BAUDRATE)/16)-1

void initUART(int baud)
{
    U1BRG = baud;            //9600 baud
    U1MODEbits.UARTEN=1;
    U1STAbits.UTXEN=1; // Enable the tx
}

void UART1PutChar(char ch)
{
    while(!U1STAbits.TRMT)
    {
        Nop(); // I like multiple Nops in case the hardware debugger skids on breakpoints
        Nop();
        Nop();
    }
    U1TXREG = ch;
    LATBbits.LATB15=1; // Debug this pin with your scope
    LATBbits.LATB15=0;
}

int main(void)

    //Set up I/O Port
    AD1PCFGL = 0xFFFF; //set to all digital I/O
   
    TRISBbits.TRISB15 = 0;   // LED pin is an output
   
//    RPINR18bits.U1RXR = 2; //UART1 receive set to RP2 // There is no RP2 on this chip
    RPINR18bits.U1RXR = 14; //UART1 receive set to RP14 (not used in this example))
   
//    RPOR0bits.RP0R = 3;         // Uart Transmit ??? // RP0 is used by programming pin PGED1
    RPOR6bits.RP13R = 3;         // Uart Transmit on RP13
 
    LATBbits.LATB15=0;  // Turn LED on to show that its not broken

    initUART(BRGVAL);       // See formula on datasheet

    __delay_ms(500);
   
    while(1)
    {
            UART1PutChar('t');
            UART1PutChar('e');
            UART1PutChar('s');
            UART1PutChar('t');
            UART1PutChar(' ');
    }
    return 0;
}


Thank you very much! I was sticking the tutorial i was reading too close, I thought that my chip had those pins (rp2) however I do thank you alot for fixing my implementation.

As a token of my appreciation, I've attached a thank you (as printed by my microcontroller)
 

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
Re: PIC24F UART
« Reply #6 on: July 09, 2016, 12:52:36 am »
Hello Again!

Its been a few hours and i'm stuck, again. This time i'm trying to receive data, and if the data is a 'p', return "p is for pineapple" (just for testing, in the actual device im designing this will use PWM to drive a motor based on something received, however it needs to send data back as well).

I am primarily a Java programmer (that's where I started) so I do have a few questions about RX with PIC.

  • I keep hearing the term "interrupts" thrown around, is this similar to event-driven programming?
  • Are interrupts required for all TX and RX operations? At the moment I have my configuration set to have interrupts on RX (IEC0bits.U1RXIE = 1) and no interrupts on TX (IEC0bits.U1TXIE = 0) is this ok?
  • Is my understanding correct: an interrupt will stop other program operation and allow for the interrupt code to run

Here's my RX method.
Code: [Select]
char UART1GetChar()
{
    char temp;
    while(IFS0bits.U1RXIF == 0);    //wait for the buffer to fill up
    temp = U1RXREG;                 //copy char
    IFS0bits.U1RXIF = 0;            //reset interrupt
    return temp;                    //return received char
}
I have changed it a bit, but it doesn't seem to work :/

Also, I'm just wondering but can you have persistent storage on PIC?
 

Online hans

  • Super Contributor
  • ***
  • Posts: 1653
  • Country: nl
Re: PIC24F UART
« Reply #7 on: July 09, 2016, 08:08:43 am »
Want to add something to your remappable pin woes.

If you look at chip diagram at the begin of the datasheet, almost every pin will have RP added with a number. You can route most peripheral functions through to any peripheral pin on this PIC24.
For a given remappable output you need to select what peripheral output source you want to use, in this case UART TX pin (source 3).
For a peripheral input source you need to choose which remappable input to listen to.

Regarding uart TX/RX interrupt:

1) Hmm, yeah, similar.. but not quite. Interrupts are generated by hardware and tied to the interrupt controller of the CPU. If a hardware interrupt fires, the CPU will look in the interrupt vector table which interrupt handler to execute. That function will be called (similar to an event), regardless of what your main application is doing (one exception: you disabled interrupts in software). The functions called through the main tree are paused.

Interrupts are high priority pieces of code and need to be short. A conventional technique is not to call any functions in an interrupt or have any long polling actions taken place. This makes sure that the interrupt is a short piece of code, thus quick. Remember; it "steals" CPU time from the main() code that can happen at any time. There are some caveats you may need to look out for, like the proper use of "volatile" and atomic read/write. These are necessary because the C compiler is not perfect, and may not be able to see any modifiers to a variable and thus assume it's constant.

2) No strictly not. Remember that most microcontroller peripherals are pretty dumb simple and rely on the software driver to tie them together. Also, some peripherals have some limitations. UART is a good example of that; you can receive a new character at any time, and waiting for a second character to be received or transmitted can take centuries in MCU land. E.g. at 9600Baud a single character will almost take 1ms to transmit. In addition, the UART can only hold a few characters at a time. In your microcontroller, there is a FIFO that hold up to 4 characters. If the FIFO overflows bytes are lost, so you need to read the data in time. A 4-byte FIFO is already quite luxurious; most 8-bit MCU's (and even some ARM micro's) don't have a FIFO and can only hold 1 character at a time.

Because of this you will see most people at least putting RX in an interrupt. That way you can have your main() code do whatever it needs to do, and once in a while come back to a buffer (that's filled by the RX interrupt) and serve the characters.
However, a scenario may happen that you need to transmit lots of TX data as well; and each byte will take 1ms to transmit. If you that messes with other main() code, you could serve interrupts for TX as well.

From what I can see your interrupt bits are correct.

3) Yes, that's correct. I'm sorry but I don't have any working code right at hand now, but an interrupt may look something like tihs:

Code: [Select]
circularbuffer uart_receive_buffer;
circularbuffer uart_transmit_buffer;

void U1_interrupt_handler(void) // this is not the actual ISR function name
{
    if (IFS0bits.U1RXIF) { // RX-not-empty interrupt fired
        uint8_t data = U1RXREG; // read register
        CircularBufferWrite data into uart_receive_buffer ; // store data in circular buffer; the main() code can read bytes from this buffer
        IFS0bits.U1RXIF = 0;
    }
    if (IFS0bits.U1TXIF) { // TX-empty interrupt fired
        if (CircularBufferEmpty uart_transmit_buffer)
        {
             U1STA = disable TX;
        }
        else
        {
             U1TXREG = CircularBufferRead from uart_transmit_buffer;
        }
        IFS0bits.U1TXIF = 0;
    }
}

For persistent storage I would look at using the internal microcontroller FLASH (although you can't erase & write FLASH 1 byte at a time); or an external EEPROM chip connected via I2C or SPI.
« Last Edit: July 09, 2016, 08:12:13 am by hans »
 

Offline 22swg

  • Frequent Contributor
  • **
  • Posts: 274
  • Country: gb
Re: PIC24F UART
« Reply #8 on: July 09, 2016, 08:39:12 am »
Hi Vampire … Firstly let me say I am no programmer, not even close, mostly my C comes from data sheets, tutorials, forums etc.... second , you are so right about PIC24 being “fun” . I started with PIC16 and assembler way back ( after fun with mpu s) , I have built several “dev” boards for PIC24 F-H-E  up to 100pin TQFP, eventually they all worked !  Your RX problem … I don't know why but polling either the buffer full flag or the interrupt flag fails to find the byte in the RX register ?

I  code a high priority ISR that reads the RX register, and puts byte in a FIFO  array and sets an “input”  flag,  poll that flag...

This is for an PIC24EP128MC202 , chatting with a HC-12 transceiver  so uart / IFS names / bits  may be different …

Code: [Select]

int SBR,SBW,SBC; // HC12 buffer pointers
//buffer Read ,Write,Current
int SerialBuf_size = 20 ;
char RX_FIFO[21] ; // U2 RX character FIFO buffer
char HC12_input ;  // poll this flag
char Serial_input ;



void __attribute__((__interrupt__, auto_psv))_U2RXInterrupt(void)
{
    while(U2STAbits.URXDA)
   {
    RX_FIFO[SBW] = U2RXREG ;
     if ((SBW+1)% SerialBuf_size != SBR)
     {
        SBW++;
        SBW %= SerialBuf_size;
     }
    }
       IFS1bits.U2RXIF = 0;
       HC12_input = 1;      // flag SERIAL DATA IN
}

char pro_buf()   // process the RX buffer
{
    char serial_in;
    serial_in = RX_FIFO[SBR];
    SBC++;
    SBR++;
    SBR %= SerialBuf_size;
    SBC %= SerialBuf_size;
    HC12_input = 0;
    return serial_in ;
}

You need to do this when your ready to RX

 IFS1bits.U2RXIF = 0;    //  clear flag.
 IEC1bits.U2RXIE = 1;  // Enable U2RX INTERRUPT.

 
« Last Edit: July 09, 2016, 08:41:46 am by 22swg »
Check your tongue, your belly and your lust. Better to enjoy someone else’s madness.
 

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
Re: PIC24F UART
« Reply #9 on: July 09, 2016, 05:56:14 pm »
Hi Vampire … Firstly let me say I am no programmer, not even close, mostly my C comes from data sheets, tutorials, forums etc.... second , you are so right about PIC24 being “fun” . I started with PIC16 and assembler way back ( after fun with mpu s) , I have built several “dev” boards for PIC24 F-H-E  up to 100pin TQFP, eventually they all worked !  Your RX problem … I don't know why but polling either the buffer full flag or the interrupt flag fails to find the byte in the RX register ?

I  code a high priority ISR that reads the RX register, and puts byte in a FIFO  array and sets an “input”  flag,  poll that flag...

This is for an PIC24EP128MC202 , chatting with a HC-12 transceiver  so uart / IFS names / bits  may be different …

Code: [Select]

int SBR,SBW,SBC; // HC12 buffer pointers
//buffer Read ,Write,Current
int SerialBuf_size = 20 ;
char RX_FIFO[21] ; // U2 RX character FIFO buffer
char HC12_input ;  // poll this flag
char Serial_input ;



void __attribute__((__interrupt__, auto_psv))_U2RXInterrupt(void)
{
    while(U2STAbits.URXDA)
   {
    RX_FIFO[SBW] = U2RXREG ;
     if ((SBW+1)% SerialBuf_size != SBR)
     {
        SBW++;
        SBW %= SerialBuf_size;
     }
    }
       IFS1bits.U2RXIF = 0;
       HC12_input = 1;      // flag SERIAL DATA IN
}

char pro_buf()   // process the RX buffer
{
    char serial_in;
    serial_in = RX_FIFO[SBR];
    SBC++;
    SBR++;
    SBR %= SerialBuf_size;
    SBC %= SerialBuf_size;
    HC12_input = 0;
    return serial_in ;
}

You need to do this when your ready to RX

 IFS1bits.U2RXIF = 0;    //  clear flag.
 IEC1bits.U2RXIE = 1;  // Enable U2RX INTERRUPT.

 

I tried this solution, i had to change the UART to refer to the 1st UART module because my pic does not have 2 UART modules. Either way I couldn't get it to work.

Want to add something to your remappable pin woes.

If you look at chip diagram at the begin of the datasheet, almost every pin will have RP added with a number. You can route most peripheral functions through to any peripheral pin on this PIC24.
For a given remappable output you need to select what peripheral output source you want to use, in this case UART TX pin (source 3).
For a peripheral input source you need to choose which remappable input to listen to.

Regarding uart TX/RX interrupt:

1) Hmm, yeah, similar.. but not quite. Interrupts are generated by hardware and tied to the interrupt controller of the CPU. If a hardware interrupt fires, the CPU will look in the interrupt vector table which interrupt handler to execute. That function will be called (similar to an event), regardless of what your main application is doing (one exception: you disabled interrupts in software). The functions called through the main tree are paused.

Interrupts are high priority pieces of code and need to be short. A conventional technique is not to call any functions in an interrupt or have any long polling actions taken place. This makes sure that the interrupt is a short piece of code, thus quick. Remember; it "steals" CPU time from the main() code that can happen at any time. There are some caveats you may need to look out for, like the proper use of "volatile" and atomic read/write. These are necessary because the C compiler is not perfect, and may not be able to see any modifiers to a variable and thus assume it's constant.

2) No strictly not. Remember that most microcontroller peripherals are pretty dumb simple and rely on the software driver to tie them together. Also, some peripherals have some limitations. UART is a good example of that; you can receive a new character at any time, and waiting for a second character to be received or transmitted can take centuries in MCU land. E.g. at 9600Baud a single character will almost take 1ms to transmit. In addition, the UART can only hold a few characters at a time. In your microcontroller, there is a FIFO that hold up to 4 characters. If the FIFO overflows bytes are lost, so you need to read the data in time. A 4-byte FIFO is already quite luxurious; most 8-bit MCU's (and even some ARM micro's) don't have a FIFO and can only hold 1 character at a time.

Because of this you will see most people at least putting RX in an interrupt. That way you can have your main() code do whatever it needs to do, and once in a while come back to a buffer (that's filled by the RX interrupt) and serve the characters.
However, a scenario may happen that you need to transmit lots of TX data as well; and each byte will take 1ms to transmit. If you that messes with other main() code, you could serve interrupts for TX as well.

From what I can see your interrupt bits are correct.

3) Yes, that's correct. I'm sorry but I don't have any working code right at hand now, but an interrupt may look something like tihs:

Code: [Select]
circularbuffer uart_receive_buffer;
circularbuffer uart_transmit_buffer;

void U1_interrupt_handler(void) // this is not the actual ISR function name
{
    if (IFS0bits.U1RXIF) { // RX-not-empty interrupt fired
        uint8_t data = U1RXREG; // read register
        CircularBufferWrite data into uart_receive_buffer ; // store data in circular buffer; the main() code can read bytes from this buffer
        IFS0bits.U1RXIF = 0;
    }
    if (IFS0bits.U1TXIF) { // TX-empty interrupt fired
        if (CircularBufferEmpty uart_transmit_buffer)
        {
             U1STA = disable TX;
        }
        else
        {
             U1TXREG = CircularBufferRead from uart_transmit_buffer;
        }
        IFS0bits.U1TXIF = 0;
    }
}

For persistent storage I would look at using the internal microcontroller FLASH (although you can't erase & write FLASH 1 byte at a time); or an external EEPROM chip connected via I2C or SPI.

Hm, I do like your solution, so I combined it with 22swg's. I made a FIFO structure, and re-implemented.

Still does not work. For now I'm just trying to make a serial repeater.

My main.c
Code: [Select]
#pragma config OSCIOFNC = OFF           // Primary Oscillator Output Function (OSC2 pin has clock out function)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
#pragma config IOL1WAY = OFF            // IOLOCK Protection (Allow Multiple Re-configurations)

#include <xc.h>

#define FCY 7370000/2 // All dsPIC33s, a few PIC24s
#include <libpic30.h>
#include "FIFOheader.h"

#define BAUDRATE 9600
#define BRGVAL ((FCY/BAUDRATE)/16)-1

// Function prototypes
extern void initUART(int baud);
extern void  UART1PutChar(char Ch);
char getRXBuff();

int main(void) {
    //Set up I/O Port
    AD1PCFGL = 0xFFFF; //set to all digital I/O

    TRISBbits.TRISB15 = 0; // LED pin is an output

    RPINR18bits.U1RXR = 14; //UART1 receive set to RP14

    RPOR6bits.RP13R = 3; // Uart Transmit on RP13

    initUART(BRGVAL); // See formula on datasheet

    while (1) {
        UART1PutChar(getRXBuff());
    }
    return 0;
}

void initUART(int baud) {
    /*Enable Interrupts*/
    IEC0bits.U1RXIE = 1; // Interrupt on RX allowed
    IEC0bits.U1TXIE = 0; // No interrupts on TX

    /*Initialize UART1*/
    U1BRG = baud; //9600 baud
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN = 1; // Enable the TX

}

void UART1PutChar(char ch) {
    while (!U1STAbits.TRMT) {
        Nop();
        Nop();
        Nop();
    }
    U1TXREG = ch;
    LATBbits.LATB15 = 1; // Debug this pin with your scope
    LATBbits.LATB15 = 0;
}


void __attribute__((__interrupt__)) _U1RXInterrupt(void); // Declaration

void __attribute__((__interrupt__, auto_psv)) _U1RXInterrupt(void) {
    if (IFS0bits.U1RXIF) { // RX-not-empty interrupt fired
        char data = U1RXREG; // read register
        // store data in circular buffer; the main() code can read bytes from this buffer
        add(data);
        IFS0bits.U1RXIF = 0; // reset interrupt
    }
}

char getRXBuff() // process the RX buffer
{
    if(isEmpty() == 1) {
        return 0;
    } else {
        return get();
    }
}

My FIFO.c

Code: [Select]
/*
 * File:   FIFO.c
 * Author: netwinder
 *
 * Created on July 9, 2016, 12:25 PM
 */

#include "FIFOheader.h"

char FIFOArray[32];
int tail = 0;

// TOHEADER


void add(char data) {
    addLast(data);
}

void addLast(char data) {
    if (tail == 32) {
        tail = 0;
    }
    FIFOArray[tail] = data;
    tail++;
}

char get() {
    if(isEmpty() == 1) {
        return 0;
    } else {
        return getFirst();
    }
}

char getFirst() {
    char temp = FIFOArray[tail];
    FIFOArray[tail] = 0; // I would use null but i can't
    tail--;
    return temp;
}

int isEmpty() {
    if(tail == 0) {
        return 1;
    } else {
        return 0;
    }
}

The FIFO.c header file
Code: [Select]
/* Microchip Technology Inc. and its subsidiaries.  You may use this software
 * and any derivatives exclusively with Microchip products.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS".  NO WARRANTIES, WHETHER
 * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
 * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
 * PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
 * WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.
 *
 * IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
 * INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
 * WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
 * BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE
 * FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS
 * IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF
 * ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 * MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
 * TERMS.
 */

/*
 * File:   
 * Author:
 * Comments:
 * Revision history:
 */

// This is a guard condition so that contents of this file are not included
// more than once. 
#ifndef XC_HEADER_TEMPLATE_H
#define XC_HEADER_TEMPLATE_H

#include <xc.h> // include processor files - each processor file is guarded. 

// TODO Insert appropriate #include <>

// TODO Insert C++ class definitions if appropriate

// TODO Insert declarations

// Comment a function and leverage automatic documentation with slash star star
/**
    <p><b>Function prototype:</b></p>
 
    <p><b>Summary:</b></p>

    <p><b>Description:</b></p>

    <p><b>Precondition:</b></p>

    <p><b>Parameters:</b></p>

    <p><b>Returns:</b></p>

    <p><b>Example:</b></p>
    <code>
 
    </code>

    <p><b>Remarks:</b></p>
 */
// TODO Insert declarations or function prototypes (right here) to leverage
// live documentation

void add(char data);
void addLast(char data);
int isEmpty();
char get();
char getFirst();

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

    // TODO If C++ is being used, regular C code needs function names to have C
    // linkage so the functions can be used by the c code.

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* XC_HEADER_TEMPLATE_H */


Sorry for the raw code dump - i've been working at this structure for a few hours and I've been trying to debug the RX side of things to no avail.
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5324
  • Country: gb
Re: PIC24F UART
« Reply #10 on: July 09, 2016, 07:08:14 pm »
I've debugged the code.

First, the code is continually writing to the UART, whether there's a character or not. I am not sure if this is what you want, but right now it's just sending nuls ('\0').

Second, the ISR is being fired, and the character is read correctly from the RP14 pin.

The buffer stuff isn't right though. you need a tail and a head, there's only a tail implemented.

I'd get your buffer stuff working in a test harness first, maybe just on a PC in portable code, then port it.

One further thing, it's fairly common nowadays to #include <stdint.h> so you can use the standard int8_t, uint8_t etc. This will help in portability.

Whether you use a ciruclar buffer or a queue is up to you: I tend to use boilerplate queues for UART stuff.

Also be careful of how much stuff you do in your ISR. Generally calling nested functions, or calling functions at all, is avoided, but this is a rule of thumb. Of course we all like modular code, but there is a balance between academically pleasing niceties and the reality of generating tight code that works well. In this case it's not a big deal, but when you start running at higher speed interrupts, it's worth bearing in mind, as you run the risk that your main() superloop is rarely getting any attention.
 

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
Re: PIC24F UART
« Reply #11 on: July 09, 2016, 08:41:25 pm »
Hello!

I got it working, thank you howardlong for pointing me in the right direction. I can now successfuly receive some text and send it back.

Kind of.

Code: [Select]
#pragma config OSCIOFNC = OFF           // Primary Oscillator Output Function (OSC2 pin has clock out function)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
#pragma config IOL1WAY = OFF            // IOLOCK Protection (Allow Multiple Re-configurations)

#include <xc.h>

#define FCY 7370000/2 // All dsPIC33s, a few PIC24s
#include <libpic30.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define BAUDRATE 9600
#define BRGVAL ((FCY/BAUDRATE)/16)-1
#define QUEUESIZE 64

// Function prototypes
extern void initUART(int baud);
extern void  UART1PutChar(char Ch);
char getRXBuff();
struct Queue* createQueue(unsigned capacity);
int isFull(struct Queue* queue);
int isEmpty(struct Queue* queue);
void enqueue(struct Queue* queue, int item);
int dequeue(struct Queue* queue);
int front(struct Queue* queue);
int rear(struct Queue* queue);

struct Queue* queue;

int main(void) {
    //Set up I/O Port
    AD1PCFGL = 0xFFFF; //set to all digital I/O

    TRISBbits.TRISB15 = 0; // LED pin is an output

    RPINR18bits.U1RXR = 14; //UART1 receive set to RP14

    RPOR6bits.RP13R = 3; // Uart Transmit on RP13

    initUART(BRGVAL); // See formula on datasheet

    while (1) {
        // Silly routine just to occupy the cpu
        printf("TX\n");
        __delay_ms(1000);
    }
    return 0;
}

void initUART(int baud) {
    queue = createQueue(QUEUESIZE);
    /*Enable Interrupts*/
    IEC0bits.U1RXIE = 1; // Interrupt on RX allowed
    IEC0bits.U1TXIE = 0; // No interrupts on TX

    /*Initialize UART1*/
    U1BRG = baud; //9600 baud
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN = 1;

}

void UART1PutChar(char ch) {
    while (!U1STAbits.TRMT) {
        Nop();
        Nop();
        Nop();
    }
    U1TXREG = ch;
}

void __attribute__((__interrupt__)) _U1RXInterrupt(void); // Declaration

void __attribute__((__interrupt__, auto_psv)) _U1RXInterrupt(void) {
    if (IFS0bits.U1RXIF) { // RX-not-empty interrupt fired
        char data = U1RXREG; // read register
        // store data in circular buffer; the main() code can read bytes from this buffer
        enqueue(queue, data);
        char c = (char)dequeue(queue) + 0;
        UART1PutChar(c);
        printf("\n");
        IFS0bits.U1RXIF = 0; // reset interrupt
    }
}

char getRXBuff() // process the RX buffer
{
    if(isEmpty(queue) == 1) {
        return 0;
    } else {
        return dequeue(queue);
    }
}

/* --- QUEUE IMPLEMENTATION --- */

// A structure to represent a queue
struct Queue
{
    int front, rear, size;
    unsigned capacity;
    char* array;
};

// function to create a queue of given capacity. It initializes size of
// queue as 0
struct Queue* createQueue(unsigned capacity)
{
    struct Queue* queue = (struct Queue*) malloc(sizeof(struct Queue));
    queue->capacity = capacity;
    queue->front = queue->size = 0;
    queue->rear = capacity - 1;  // This is important, see the enqueue
    queue->array = (int*) malloc(queue->capacity * sizeof(int));
    return queue;
}

// Queue is full when size becomes equal to the capacity
int isFull(struct Queue* queue)
{  return (queue->size == queue->capacity);  }

// Queue is empty when size is 0
int isEmpty(struct Queue* queue)
{  return (queue->size == 0); }

// Function to add an item to the queue.  It changes rear and size
void enqueue(struct Queue* queue, int item)
{
    if (isFull(queue))
        return;
    queue->rear = (queue->rear + 1)%queue->capacity;
    queue->array[queue->rear] = item;
    queue->size = queue->size + 1;
}

// Function to remove an item from queue.  It changes front and size
int dequeue(struct Queue* queue)
{
    if (isEmpty(queue)){
        return INT_MIN;
    }
    int item = queue->array[queue->front];
    queue->front = (queue->front + 1)%queue->capacity;
    queue->size = queue->size - 1;
    return item;
}

// Function to get front of queue
int front(struct Queue* queue)
{
    if (isEmpty(queue))
        return INT_MIN;
    return queue->array[queue->front];
}

// Function to get rear of queue
int rear(struct Queue* queue)
{
    if (isEmpty(queue))
        return INT_MIN;
    return queue->array[queue->rear];
}

Here's the output of me sending the text "hi" through a serial console
$
$
TX
TX

etc. Should I change the queue to store the data as  a char instead of an int? Because casting the int to char isn't working.
 

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
Re: PIC24F UART
« Reply #12 on: July 09, 2016, 09:01:37 pm »
Making progress...
I probed the TX of the chip then sent a load of data to the RX, and it returned the same amount of bits... Just they were all null, I'll review my queue implementation.
 

Offline 22swg

  • Frequent Contributor
  • **
  • Posts: 274
  • Country: gb
Re: PIC24F UART
« Reply #13 on: July 09, 2016, 09:19:29 pm »

        UART1PutChar(c);
        printf("\n");
My stab would be that there's too much going on in here .... Just save the byte , set a flag , reset the RX IF , and get out :) 

Additional , as soon as you enable the RX interrupt you can get an RX interrupt immediately ! , clear the IF first.     
« Last Edit: July 09, 2016, 09:26:56 pm by 22swg »
Check your tongue, your belly and your lust. Better to enjoy someone else’s madness.
 

Offline netwinderTopic starter

  • Regular Contributor
  • *
  • Posts: 69
  • Country: ca
Re: PIC24F UART
« Reply #14 on: July 09, 2016, 11:51:13 pm »
Hello,

Its fixed. I can now send and recieve, here is the code completed with a new queue data structure (i figured the old one with pointers was too compilicated) so it got replaced with arrays.

Code: [Select]
#pragma config OSCIOFNC = OFF           // Primary Oscillator Output Function (OSC2 pin has clock out function)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)
#pragma config IOL1WAY = OFF            // IOLOCK Protection (Allow Multiple Re-configurations)

#include <xc.h>

#define FCY 7370000/2 // All dsPIC33s, a few PIC24s
#include <libpic30.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define BAUDRATE 9600
#define BRGVAL ((FCY/BAUDRATE)/16)-1
#define QUEUESIZE 64

// Function prototypes
extern void initUART(int baud);
extern void UART1PutChar(char Ch);
char getRXBuff();
int isEmpty();
void enqueue(char item);
char dequeue();

int main(void) {
    //Set up I/O Port
    AD1PCFGL = 0xFFFF; //set to all digital I/O

    TRISBbits.TRISB15 = 0; // LED pin is an output

    RPINR18bits.U1RXR = 14; //UART1 receive set to RP14

    RPOR6bits.RP13R = 3; // Uart Transmit on RP13

    initUART(BRGVAL); // See formula on datasheet

    while (1) {
        if (isEmpty() == 1) {
            __delay_ms(1);
        } else {
            if(getRXBuff() == 'p'){
                printf("p is for pineapple\n");
            }
        }
    }
    return 0;
}

void initUART(int baud) {
    /*Enable Interrupts*/
    IEC0bits.U1RXIE = 1; // Interrupt on RX allowed
    IEC0bits.U1TXIE = 0; // No interrupts on TX

    /*Initialize UART1*/
    U1BRG = baud; //9600 baud
    U1MODEbits.UARTEN = 1;
    U1STAbits.UTXEN = 1;

}

void UART1PutChar(char ch) {
    while (!U1STAbits.TRMT) {
        Nop();
        Nop();
        Nop();
    }
    U1TXREG = ch;
}

void __attribute__((__interrupt__)) _U1RXInterrupt(void); // Declaration

void __attribute__((__interrupt__, auto_psv)) _U1RXInterrupt(void) {
    if (IFS0bits.U1RXIF) { // RX-not-empty interrupt fired
        char data = U1RXREG; // read register
        // store data in circular buffer; the main() code can read bytes from this buffer
        enqueue(data);
        IFS0bits.U1RXIF = 0; // reset interrupt
    }
}

char getRXBuff() // process the RX buffer
{
    if (isEmpty() == 1) {
        return 0;
    } else {
        return dequeue();
    }
}

/* --- QUEUE IMPLEMENTATION --- */

// A structure to represent a queue
char queueData[QUEUESIZE];
int head = 0;
int tail = 0;
int refactoredSize = 0;

// Queue is empty when size is 0

int isEmpty() {
    if (refactoredSize == 0) {
        return 1;
    } else {
        return 0;
    }
}

void enqueue(char item) {
    if (refactoredSize == sizeof (queueData)) {
        return;
    } else {
        queueData[tail] = item;
        tail = (tail + 1) % sizeof (queueData);
        refactoredSize++;
    }
}

char dequeue() {
    if (isEmpty == 1) {
        return 0;
    } else {
        char temp = queueData[head];
        queueData[head] = 0;
        head = (head + 1) % sizeof (queueData);
        refactoredSize--;
        return temp;
    }
}

There is one issue - in this code when 'p' is sent it responds p is for pineapple. If i probe the TX from the chip and decode it with my scope, it reads correctly. However In the serial console, its gibberish.

I'll resolve this - but for now the problems are solved.Thanks everybody for the insight  :)
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf