Cornell University ECE4760
UART serial
PIC32MX250F128B

Serial Communication

The UART is used for console comunnication of serial data. See below for physical connections.

  1. DMA UART in Protothreads
    Protothreads is a very light-weight, stackless, threading library written entirely as C macros by Adam Dunkels. As such, it is trivial to move to PIC32. Adam Dunkels' documentation is very good and easy to understand. There is support for a thread to wait for an event, spawn a thread, and use semaphores. The Protothreads system is a cooperative multithread system. As such, there is no thread preemption. All thread switching is at explicit wait or yield statement. There is no scheduler. You can write your own, or just use a simple round-robin scheme, calling each thread in succession in main. Because there is no preemption, handling shared variables is easier because you always know exactly when a thread switch will occur. Because there is no separate stack for each thread, the memory footprint is quite small, but using automatic (stack) local variables must be avoided. You can use static local variables. Protothreads uses a switch-statement type construct to handle thread switching, so it is not possible to embed a thread-wait statement in a switch stanza. You must read sections 1.3-1.6 of the reference manual to see all of the implementation details. I hacked some of Dunkels' examples shown below and added:
    Current Version:
    To run protothreads you need to download pt_cornell.h. The Example1 test code also requires a UART connection to a terminal, as explained in a project further down the page. The test code toggles three i/o pins and supports a small user interface through the UART.It also emits three different amplitude debugging pulses on pin 25. By default this version of protothreads starts 32-bit timer45 and uses a timer ISR to count milliseconds. The following table has the protothread macro extensions and functions I wrote for the PIC32 and which are included in the header file..
    Protothreads function

    Description

     

    PT_YIELD_TIME_msec(delay_time) Causes the current thread to yield (stop executing) for the delay_time in milliseconds. The time is derived from a 1 mSec ISR running from timer5.
    PT_GET_TIME() Returns the current millisecond count since boot time. Overflows in about 5 weeks. The time is derived from a 1 mSec ISR running from timer5.
    PT_RATE_INIT() Sets up variables for the optional rate scheduler
    PT_RATE_LOOP() House keeping for the optional rate scheduler
    PT_RATE_SCHEDULE(f,rate) For thread f, set the rate=0 to execute always, rate=1 to execute every other traversal for PT_RATE_LOOP, rate=2 to every fourth traversal, rate=3 to every 8th, and rate=4 to every 16th.
    PT_DEBUG_VALUE(level, duration) Causes a voltage level from 0 to 15 (1 implies ~150 mV) to appear at pin 25 (CVrefOut) for duration microseconds (approximately). Zero duration means hold the voltage until changed by another call.
    int PT_GetSerialBuffer(struct pt *pt) A thread which is spawned to get nonblocking string input from UART2. String is returned in
    char PT_term_buffer[max_chars]. If more than one thread can spawn this thread, then there must be semaphore protection. Control returns to the scheduler after every character is received. The thread dies when it recieves an <enter>
    int PutSerialBuffer(struct pt *pt) A thread which is spawned to send a string input from UART2. String to be sent is in
    char PT_send_buffer[max_chars]. If more than one thread can spawn this thread, then there must be semaphore protection. Control returns to the scheduler after every character is loaded to be sent. The thread dies after it sends the entire string.
    int PT_DMA_PutSerialBuffer(struct pt *pt) A thread which is spawned to send a string input from UART2. String to be sent is in
    char PT_send_buffer[max_chars]. If more than one thread can spawn this thread, then there must be semaphore protection. Control returns to the scheduler immediately. The thread dies after it sends the entire string.
    void PT_setup (void) Configures system frequency, UART2, a DMA channel 1 for the UART2 send, timer5, and the debug pin Vref controller.


    =============================
    To run these older examples you need to download software from Dunkels' site or use a local copy. Most examples also require a UART connection to a terminal, as explained in a project further down the page.
    Older examples:
    -- The first example has two threads executing at a rate based on a hardware timer ISR, which generates a millisecond time counter. Each thread yields for a waiting time and when executing prints the thread number and time. Thread 1 executes once per second. Thread 2 executes every 4 seconds. Main just sets up the timer ISR and UART, then inintialzes the threads and schedules them.
    -- The second example has three threads. Threads 1 and 2 wait on semaphores, each of which is signaled by the other thread. The two threades therefore alternate. Thread 3 just executes every few seconds. I defined an new macro to make it easier for a thread to wait for a specific time. PT_YIELD_TIME(wait_time) takes the wait time parameter and uses a local variable and the millisceond timer variable to yield the processor to another thread for wait_time milliseconds. The second example also has a small routine to compute approximate microseconds since reset and return it as a 64-bit long long int.
    #define PT_YIELD_TIME(delay_time) \
        do { static int time_thread; \
        PT_YIELD_UNTIL(pt, milliSec >= time_thread); \
        time_thread = milliSec + delay_time ;} while(0);

    -- The third example has three threads. Threads 1 and 2 wait on semaphores, each of which is signaled by the other thread. The two threads therefore alternate. Thread 3 takes input from a serial terminal. The actual input routine is a thread which is spawned by thread 3. Thread 3 then waits for the input thread to terminate which it does when the human presses <enter>. The input thread yields the processor while it is waiting for the slow human to type each character, so other threads do not stall. The key statment is below which causes protothreads to wait/yield on a hardware flag. The flag is defined as part of plib.h.
    PT_YIELD_UNTIL(pt, UARTReceivedDataIsAvailable(UART2));
    Note that the spawn command
    PT_SPAWN(pt, &pt_input, GetSerialBuffer(&pt_input) );
    initializes the input thread and schedules it. The three parameters are the current thread structure, a pointer to the spawned thread, and the actual thread function. If more than one thread is using serial input, then the spawn command should be surrounded by semaphore wait/signal commands because GetSerialBuffer is not reentrant.
    -- The fourth example investigates non-blocking UART transmit. In a printf, there is a waitloop for each character. We can replace that with thread yield on a per character basis. Doing this speeds up processing a factor of 2 or so. But how fast is the swtich between two threads? Is it worth a thread yield on every character? Commenting out all UART code and just waiting/signaling on a semaphore between thread 1 and thread 2 gives a switch time between threads (twice) of 2.1 microcseconds or about 126 cpu cycles. This value includes the signaling, waiting, and thread switch code two times (thread 1 to thread 2 and back). For a 1 mSec charcter transmit time, the thread switch is worth the overhead.
    -- The fifth example implements a terminal command interface in thread 3 using non-blocking UART send/receive. Thread 1 and 2 toggle and are dependent upon signalling each other unless turned off by a flag from the interactive input. . Thread 4 just toggles at a fixed rate, unless it is turned off by the interactive input, working through the scheduler. The code assumes that port pins B0, A0, and A1 are connected to LEDs (with 300 ohm resistor to ground). Hitting the <enter> key to finish a command results in a 9 microSec pause in the toggling of the other threads.
    There are 8 commands:

    command effect
    t1 time sets blink rate of thread 1/2 to time
    t2 time sets blink rate of thread 4 to time
    g1 starts thread 1/2 blink
    s1 stops thread 1/2 blink
    g2 starts thread 4 blink
    s2 stops thread 4 blink
    k kills the interactive input until RESET
    p prints the current blink times

    -- The sixth example runs the same interface as example five, but uses a DMA channel to drive the UART output with no software overhead. The DMA pattern matching feature detects the end of a string to stop the UART automatically. Using the DMA transfer allows a per-string thread yield, rather than a per-character thread yield. The code assumes that port pins B0, A0, and A1 are connected to LEDs (with 300 ohm resistor to ground). The thread switch after hitting <enter> now takes 5 microSec. With both t1 and t2 set to 1 milliSec, the dispersion in actual times for both is less than 10 microSec (<1%).
    -- The seventh example adds a microsecond resolution yield option. The option is marginally useful down to about 10 microseconds, where the timing uncertainty reaches about 10%. At 100 microseconds the accuracy is good. This means that you could attempt audio synthesis in a thread at 10 KHz sample rate. Thread 4 is timed by the microsecond timer. With three threads running below100 microSec repeat rate, the system starts to miss events. The previous PT_YIELD_TIME macro has been replaced by two. One for millisecond timing and one for microsecond timing. The millisecond timer overflows about once/month. The microsecond timer overflows every 64 milliseconds. The maximum time delay using the microsecond timer is 64000 microseconds.

    // macro to time a thread execution interval
    #define PT_YIELD_TIME_msec(delay_time) \
        do { static int time_thread; \
        time_thread = milliSec + delay_time ; \
        PT_YIELD_UNTIL(pt, milliSec >= time_thread); \
        } while(0);
    // macro to time a thread execution interveal
    // parameter is in MICROSEC < 64000
    //ReadTimer2()
    #define PT_YIELD_TIME_usec(delay_time) \
        do { static unsigned int time_thread, T3, c ; \
          time_thread = T3 + delay_time ; c = 0;\
          if(time_thread >= 0xffff) { c = 0xffff-T3; }\
          PT_YIELD_UNTIL(pt, ((ReadTimer3()+c)& 0xffff) >= ((time_thread+c) & 0xffff)); \
          T3 = ReadTimer3() ;\
        } while(0); 

    -- The eighth example introduces a minimal scheduler which allows each thread to execute at a rate determined as a fraction of full speed. The default protothreads thread swap is so fast that it a challange to introduce scheduling which does not slow down thread execution rates. The approach taken is to allow some threads to execute every time through the main while-loop, but allow others to only execute at 1/2, 1/4, 1/8, or 1/16 of the times through the main loop. The approach is consistent with a nonpremptive thread system and gives better execution consistency if one thread has to execute at a much higher rate than the others. Rate 0 executes every time throught the loop, rate 1 every other time, 2 every four times, rate 3 every 8 times, and rate 4 every 16 times through the main while-loop. Any other value freezes the thread execution. With thread 4 executing at a nominal 10 microSec period, the actual time varys from 11 to 13 microSec, but the actual time can vary widely depending on the exact interval picked due to coincidence with other processes. This version also fixes the microsecond timer by using timer45 as a 32-bit counter.
    -- Finally we get to something like a final version of the code.

  2. UART low level setup and serial communication
    -- The XC32 compiler libraries treat UART2 as standard-in and standard-out. Using the examples from (ref 1) and from
    C:\Program Files (x86)\Microchip\xc32\v1.31\examples\plib_examples
    I wrote a minimal UART interface example which can get individual characters, get strings, and use printf. I could not make scanf work, but getting a string and using sscanf is a workaround. The UART input/output is not routed by default. You must specify a peripherial pin select (PPS) option as described in
    http://people.ece.cornell.edu/land/courses/ece4760/PIC32/Microchip_stuff/2xx_datasheet.pdf

    Table 11-1 which gives input pin mapping, and Table 11.2 which gives output pin mapping. The minimal setup seems to be:
        // specify PPS group, signal, logical pin name
        PPSInput (2, U2RX, RPB11); //Assign U2RX to pin RPB11 -- Physical pin 22 on 28 PDIP
        PPSOutput(4, RPB10, U2TX); //Assign U2TX to pin RPB10 -- Physical pin 21 on 28 PDIP
        // init the uart2
        UARTConfigure(UART2, UART_ENABLE_PINS_TX_RX_ONLY);
        UARTSetLineControl(UART2, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
        UARTSetDataRate(UART2, PB_FREQ, BAUDRATE);
        UARTEnable(UART2, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));
    -- All the setup functions are documented in the MPLAB-X help files under XC32-peripherial libraries. There is one helper function in the example, GetDataBuffer(), which buffers the input from the UART and echos the input, until an <enter> keystroke occurs, then zero-terminates the string and emits a CRLF to position the cursor on the next line. Notice that the function is blocking because it waits, possibly forever, in the while(!UARTReceivedDataIsAvailable(UART2)){}; for the user to type.
    -- The GetDataBuffer() function above is a little annoying because you cannot backspace over a mistake. Adding a backspace is easy (code) but you have to make sure that your terminal uses control-H (ascii code 0x08) as the backspace code. In PuTTY you have to right-click the title bar, choose Change Settings..., then choose the Terminal-Keyboard panel and choose the control-H backspace.
    -- The physical interface to the PC was a Sparkfun CP2102 USB-UART interface with the
    CP2102 TX pin hooked to MCU pin 22 (U2RX), the CP2102 RX pin hooked to MCU pin 21 (U2TX),
    and of course, the CP2102 ground pin hooked to MCU pin 27 (or pin 8, see ref 8-2).
    the connections


References:

  1. Beginner's Guide to Programming the PIC32 Paperback by Thomas Kibalo
    and more info
  2. Programming 32-bit Microcontrollers in C: Exploring the PIC32 by Lucio Di Jasio
    and more info
  3. PIC32 Architecture and Programming by Johm Loomis Numb3rs
  4. Intro to PIC32 architectture
  5. PIC32 tutorials
  6. UMass M5 PIC32 tutorials and specifically for the PIC32MX220
  7. Northwestern University mechatronics design wiki:
    1. code examples,
    2. benchmarking,
    3. Embedded programming on PIC32
  8. Tahmid's Blog
  9. chipKit
  10. Arduino library for PIC32
    1. Microstick configuration
    2. project zip
  11. DSP experiments and more and
  12. RTOS
    1. http://www.freertos.org/ and Microchip PIC32 FreeRTOS Reference Designs and MPLABX and ECE443 ref
    2. TNKernel
    3. ERIKA Enterprise
    4. Q-Kernel
    5. Protothreads by Adam Dunkels
    6. Protothreads -- similar to Dunkels, but different implementation only for GCC
  13. MIPS32 docs
    1. Architecture
    2. instruction set
    3. privliged resources
  14. Microchip Docs
    1. PIC32MX250F128B PDIP pinout by pin
    2. PIC32MX250F128B ::: Signal Names=>Pins ::: 1, 2, 3, 4, 5, 6, 7 PDIP highlighted in green (for PPS see next tables)
    3. PIC32MX250F128B Peripheral Pin Select (PPS) input table
      example: UART receive pin ::: specify PPS group, signal, logical pin name
      PPSInput(2, U2RX, RPB11); //Assign U2RX to pin RPB11 -- Physical pin 22 on 28 PDIP
    4. PIC32MX250F128B Peripheral Pin Select (PPS) output table
      example: UART transmit pin ::: specify PPS group, logical pin name, signal
      PPSOutput(4, RPB10, U2TX); //Assign U2TX to pin RPB10 -- Physical pin 21 on 28 PDIP
    5. PIC32MX1XX/2XX Interrupt Table 1, 2 and int_1xx_2xx.h -- interrupt IRQ names and vector names
    6. plib.h names of peripherial header files
      -----------------------------------------------------------------
    7. Getting started with PIC32
    8. MPLABX IDE users guide
    9. PICkit3 Users Guide and poster
    10. 32_bit peripherials library
    11. 32 bit language tools and libraries including C libraries, DSP, and debugging tools
    12. XC32 Compiler Users Guide
    13. microstickII pinout
    14. PIC32 reference manual
      and more from Northwestern University mechatronics design wiki, PIC32 page
    15. MIPS-M4K Core
    16. 2xx_datasheet
    17. Microchip doc site on this page choose Documentation from the left column.
      The Reference Manual is particuarly useful
    18. USB Embedded Host Stack
    19. chipKIT (PIC32 arduino library)
    20. code examples (choose PIC32 in product family dropdown)
    21. code libraries (choose PIC32 in product family dropdown)
    22. application notes (choose PIC32 in Select a Product Family panel)
    23. Harmony for PIC32 -- docs --
    24. Microchip TCP/IP Stack Application Note
  15. External Refs back to this work
    1. http://dangerousprototypes.com/2014/07/15/pic32-oscilloscope/
    2. http://hackedgadgets.com/2014/07/14/pic32-oscilloscope/

Copyright Cornell University June 23, 2015