Jump to content
  • 0

chipKIT Pro MX4, Harmony, UART buffer queue filling, but nothing sent


Andy Harris

Question

Hello,  I'm trying to get a Hello World app to work using a chipKit Pro MX4.  I've followed along with example code written for a Starter Kit board changing and making adjustments to the MPLAB X Harmony configurator as I go along, although there really isn't that much difference between the two as the sample.x project is written for a PIC32MX795.  I'm using the USART driver with the following code in system_config.h (See the configuration code generated by Harmony and stored in the system_config.h file).  My state machine simply adds a bunch of strings to the Transmit Buffer Queue using the DRV_USART_BufferAddWrite function provided by the driver module.  This seems to be working successfully as I do receive a valid handle from the function.  The problem is there is nothing being sent.  I have, in addition to using the trainer board, brought this up using the simulator and there really is nothing being  sent on UART 1.  So, when the Transmit Buffer Queue fills up with buffers to be sent the USART driver begins denying any further attempt to add message to the queue.  All of that works as expected.  The sample application seems to be doing nothing else significant and when run on simulator (since I have not trainer board for that part) I see the text appear in the USART 2 Output window.  (Note: the MX4 board uses UART 1 for the J8 connector and so I've modified to use UART 1 instead of 2... no big deal there.)  I've been up most of the night or all night pouring over data sheets and the like trying to figure this out, but I've come across nothing significant.  

My thinking on this is that the simulator knows nothing about the specific card and its many connections.  It knows only of the microcontroller family and part so that means that it's not really something specific to the chipKIT MX4 board, but some difference in the part itself.  We all know how terse datasheets can be, so I must be missing something basic with regard to PIC32MX vs. PIC32MZ.  That's why I have focused mainly on looking at the part's documentation.

My question is: Has anyone else done anything like this, or ran into a similar problem, or maybe someone else has been able to get this working and I'm missing something basic?  I just have been very unsuccessful at figuring this out.  I'm not new to embedded systems, but I'm an older guy trying to refresh and update my skills after being out of the industry for a long time.  I most certainly could be missing something obvious.  As I said I've been up many long nights persevering.  Any help would be greatly appreciated.  

 

This is the code generated by Harmony in the system_config.h file.  I've also included my state machine and its header file which is simple.  There isn't very much code here that wasn't generated by MPLAB Harmony Configurator, so that's adding to my being stumped.  I'm wondering if there is something deeply hidden in the Harmony framework that is actually malfunctioning.  I just can't be sure.  I look forward to your response.  Thank you much.

// *****************************************************************************
/* Clock System Service Configuration Options
*/
#define SYS_CLK_FREQ                        80000000ul
#define SYS_CLK_BUS_PERIPHERAL_1            10000000ul
#define SYS_CLK_UPLL_BEFORE_DIV2_FREQ       48000000ul
#define SYS_CLK_CONFIG_PRIMARY_XTAL         8000000ul
#define SYS_CLK_CONFIG_SECONDARY_XTAL       32768ul
 
/*** Ports System Service Configuration ***/
#define SYS_PORT_AD1PCFG        ~0xffff
#define SYS_PORT_CNPUE          0x0
#define SYS_PORT_CNEN           0x0

#define SYS_PORT_B_TRIS         0xc3ff
#define SYS_PORT_B_LAT          0x0
#define SYS_PORT_B_ODC          0x0


// *****************************************************************************
// *****************************************************************************
// Section: Driver Configuration
// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
/* USART Driver Configuration Options
*/
#define DRV_USART_INTERRUPT_MODE                    false

#define DRV_USART_BYTE_MODEL_SUPPORT                false

#define DRV_USART_READ_WRITE_MODEL_SUPPORT          true

#define DRV_USART_BUFFER_QUEUE_SUPPORT              true

#define DRV_USART_CLIENTS_NUMBER                    1
#define DRV_USART_SUPPORT_TRANSMIT_DMA              false
#define DRV_USART_SUPPORT_RECEIVE_DMA               false
#define DRV_USART_INSTANCES_NUMBER                  1

#define DRV_USART_PERIPHERAL_ID_IDX0                USART_ID_1
#define DRV_USART_OPER_MODE_IDX0                    DRV_USART_OPERATION_MODE_NORMAL
#define DRV_USART_OPER_MODE_DATA_IDX0               
#define DRV_USART_INIT_FLAG_WAKE_ON_START_IDX0      false
#define DRV_USART_INIT_FLAG_AUTO_BAUD_IDX0          false
#define DRV_USART_INIT_FLAG_STOP_IN_IDLE_IDX0       false
#define DRV_USART_INIT_FLAGS_IDX0                   0
#define DRV_USART_BRG_CLOCK_IDX0                    10000000
#define DRV_USART_BAUD_RATE_IDX0                    9600
#define DRV_USART_LINE_CNTRL_IDX0                   DRV_USART_LINE_CONTROL_8NONE1
#define DRV_USART_HANDSHAKE_MODE_IDX0               DRV_USART_HANDSHAKE_NONE
#define DRV_USART_XMIT_INT_SRC_IDX0                 INT_SOURCE_USART_1_TRANSMIT
#define DRV_USART_RCV_INT_SRC_IDX0                  INT_SOURCE_USART_1_RECEIVE
#define DRV_USART_ERR_INT_SRC_IDX0                  INT_SOURCE_USART_1_ERROR

#define DRV_USART_XMIT_QUEUE_SIZE_IDX0              18
#define DRV_USART_RCV_QUEUE_SIZE_IDX0               8


#define DRV_USART_POWER_STATE_IDX0                  SYS_MODULE_POWER_RUN_FULL

#define DRV_USART_QUEUE_DEPTH_COMBINED              26

 

 

Here's my state machine and its related code from app.c

Please forgive/ignore some of the unnecessary code I put in for debugging purposes.

void APP_Tasks ( void )
{

    static unsigned long int number = 0;
    static char string[200];
    static int messageCount;
    static DRV_HANDLE usartBufferHandle;
    int debugDummy = 1;
    char * pointerToString = &string[1];
    int stringLength;
    
    
    /* Check the application's current state. */
    switch ( appData.state )
    {
        /* Application's initial state. */
        case APP_STATE_INIT:
        /* Keep trying to open the driver until we succeed. */
        {
            messageCount = 0;
            /* open an instance of USART driver */
            appData.usartHandle = DRV_USART_Open(APP_UART_DRIVER_INDEX, DRV_IO_INTENT_WRITE);
            if (appData.usartHandle != DRV_HANDLE_INVALID ) 
            {
                /* Update the state */
                appData.state = APP_STATE_GENERATE_MESSAGE;
            }
            break;
        }

        case APP_STATE_GENERATE_MESSAGE:
        {
            char * c = NumberToString(&string[1], number, 9, true);
            *c = '\r';
            *(c + 1) = '\n';
            *(c + 2) = '\0';
            number++;
            appData.state = APP_STATE_SEND_MESSAGE;
            break;
        }
        
        case APP_STATE_SEND_MESSAGE:
        {
            stringLength = strlen(&string[1]);
            pointerToString = &string[1];
            DRV_USART_BufferAddWrite(
                    appData.usartHandle,
                    &usartBufferHandle,
                    pointerToString,
                    stringLength);
            
            if(usartBufferHandle != DRV_HANDLE_INVALID)
            {
                
                if(messageCount++ < 100001)
                {
                    appData.state = APP_STATE_GENERATE_MESSAGE;
                }
                else
                {
                    appData.state = APP_STATE_WAIT4SENT;
                }
            }
            break;
        }
        
        case APP_STATE_WAIT4SENT:
        {
            debugDummy++;
            int i = debugDummy;
            break;
        }
        
        default:
        {
            debugDummy++;
            /* PROGRAM COUNTER SHOULD NEVER REACH THIS CODE!      */
            /* TODO: Handle error in application's state machine. */
            break;
        }
    }
}

 

And finally, here is my header file from app.h.

#ifndef _APP_H
#define _APP_H

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "system_config.h"
#include "system_definitions.h"
#include "driver/driver.h"

// DOM-IGNORE-BEGIN
#ifdef __cplusplus  // Provide C++ Compatibility

extern "C" {

#endif
// DOM-IGNORE-END 

// *****************************************************************************
// *****************************************************************************
// Section: Type Definitions
// *****************************************************************************
// *****************************************************************************
#define APP_UART_DRIVER_INDEX           DRV_USART_INDEX_0

// *****************************************************************************
/* Application states

  Summary:
    Application states enumeration

  Description:
    This enumeration defines the valid application states.  These states
    determine the behavior of the application at various times.
*/

typedef enum
{
    /* Application's state machine's initial state. */
    APP_STATE_INIT=0,
    APP_STATE_GENERATE_MESSAGE,
    APP_STATE_SEND_MESSAGE,
    APP_STATE_WAIT4SENT,
} APP_STATES;


// *****************************************************************************
/* Application Data

  Summary:
    Holds application data

  Description:
    This structure holds the application's data.

  Remarks:
    Application strings and buffers are be defined outside this structure.
 */

typedef struct
{
    /* The application's current state */
    APP_STATES state;
    DRV_HANDLE usartHandle; 

    /* TODO: Define any additional data used by the application. */

} APP_DATA;


// *****************************************************************************
// *****************************************************************************
// Section: Application Callback Routines
// *****************************************************************************
// *****************************************************************************
/* These routines are called by drivers when certain events occur.
*/
    
// *****************************************************************************
// *****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************
/*******************************************************************************
  Function:
    void NumberToString (char * string, unsigned long int (number)

  Summary:
    Convert a number to a string.

  Description:
    This function will store a string containing the value of an unsigned long
    integer to a string containing the value written out in long form using
    English.
 
  Precondition:
    All other system initialization routines should be called before calling
    this routine.

  Parameters:
    None.

  Returns:
    None.

  Example:
    <code>
    NumberToString(&string, num);
    </code>

  Remarks:
    Storage string must be large enough to hold max unsigned long written out
    in English
*/
char * NumberToString (char * string, unsigned long int number, int e, bool outside);

/*******************************************************************************
  Function:
    void APP_Initialize ( void )

  Summary:
     MPLAB Harmony application initialization routine.

  Description:
    This function initializes the Harmony application.  It places the 
    application in its initial state and prepares it to run so that its 
    APP_Tasks function can be called.

  Precondition:
    All other system initialization routines should be called before calling
    this routine (in "SYS_Initialize").

  Parameters:
    None.

  Returns:
    None.

  Example:
    <code>
    APP_Initialize();
    </code>

  Remarks:
    This routine must be called from the SYS_Initialize function.
*/

void APP_Initialize ( void );


/*******************************************************************************
  Function:
    void APP_Tasks ( void )

  Summary:
    MPLAB Harmony Demo application tasks function

  Description:
    This routine is the Harmony Demo application's tasks function.  It
    defines the application's state machine and core logic.

  Precondition:
    The system and application initialization ("SYS_Initialize") should be
    called before calling this.

  Parameters:
    None.

  Returns:
    None.

  Example:
    <code>
    APP_Tasks();
    </code>

  Remarks:
    This routine must be called from SYS_Tasks() routine.
 */

void APP_Tasks( void );


#endif /* _APP_H */

//DOM-IGNORE-BEGIN
#ifdef __cplusplus
}
#endif
//DOM-IGNORE-END

/*******************************************************************************
 End of File
 */

Link to comment
Share on other sites

4 answers to this question

Recommended Posts

Hi Andy,

I apologize for taking awhile to get back to you. I don't personally know the answer to your question, but I have asked some of our applications engineers about this; they will get back to you here on the forum.

Thanks,
JColvin

Link to comment
Share on other sites

No problem.  Sometimes it's better to have to find the answer yourself, so it's no big deal.  What I've learned about Harmony UART programming so far (or at least I think I have):  If you plan to use the Buffer Queue transfer model, (my original question was an inquiry about this transfer model), make sure to use a callback function.  This is the only way to ensure that your buffer has been sent before queueing another one.  My program worked just fine after I understood that.  As far as I can tell, and I could be wrong, buffer queueing is really for when you are using multiple clients of the USART driver, so I elected to use the File I/O Type Read/Write transfer model instead.  Also I wrote a concurrent state controller to handle UART I/O.   If you're planning to try the File I/O Type Read/Write transfer model, then you may want to look at the following code.

First, I like to use a state controller I call my APP_USC (Application UART State Controller).  Within the USC is a number of interface functions.  One of these allows other parts of my application to talk to the APP_USC.  One of those interface function would be the APP_USC_Request_Transmit function.  See the next code sample.

bool APP_USART_Request_Transmit(char * string) {
    if (USCData.state == APP_USC_AWAIT_REQ) // don't accept any transmit request
    {                                       // unless the APP_USC is ready for one
        USCData.state = APP_USC_SENDING_DATA; // this gets the USC out of the APP_USC_AWAIT_REQ state
        strcpy(USCData.XmitString, string); // store the string for sending 
        USCData.bytesToSend = strlen(USCData.XmitString); // the length of the string
        USCData.bytesSent = 0; // and reset the accumulated bytes sent
        
        return true;  // tell the caller that the request is accepted
    }
    else 
    {
        return false; // hopefully the calling function is a non-blocking state controller itself;
    }                 // however, no law says that this can't just be called in a while loop
} // called to send a message

 

Then in the state machine part of the APP_USC use a pair of state sections that look like this.  (Note: the only preceding state missing from the next code sample is the APP_USC_INITIALIZING state.)

.
.
.
case APP_USC_AWAIT_REQ: // await transmit request
        {
            // this state is only exited when the module using the USC calls
            // APP_USART_Request_Transmit(...)
            break;
        } 
        
        case APP_USC_SENDING_DATA: // now send some data.  A little bit at a time until it's all been sent
        {                          // this is the important part.
            int count;
            count = DRV_USART_Write(USCData.usartHandle, 
                    &USCData.XmitString[USCData.bytesSent], 
                    USCData.bytesToSend);
            USCData.bytesToSend -= count;
            USCData.bytesSent += count;
            if(USCData.bytesToSend == 0)
            {
                USCData.state = APP_USC_AWAIT_REQ;
            }            
            break;
        }
.
.
.

In the manner described above one can be confident that the data will get through the UART intact and at great speed.  Note that regardless of your choice on setting up the USART in interrupt mode or in polled mode the above code does not change.

Harmony's learning curve is significant, but here you see I've used it to drive a Digilent chipKIT MX4 without even worrying about anything but the clock setup and the UART setup.  I forced the machine to send across the UART text strings counting in English from "zero" all the way to "three hundred seventy-seven million three hundred seventy seven thousand three hundred seventy-seven", (or some such thing), without a single flaw.  And it does this at 921600 baud which is a reasonably impressive speed.  (see the screen shot)

For the sake of completeness I'll include the Harmony-generated UART configuration found in system_config.h    This code was made by using the Harmony configurator GUI.  A process that takes about a minute once you've done it a couple of times.  As you can see I'm using it in interrupt mode right now, but you can uncheck that option and the code runs just as well.

// *****************************************************************************
// *****************************************************************************
// Section: Driver Configuration
// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
/* USART Driver Configuration Options
*/
#define DRV_USART_INTERRUPT_MODE                    true
#define DRV_USART_BYTE_MODEL_SUPPORT                false
#define DRV_USART_READ_WRITE_MODEL_SUPPORT          true
#define DRV_USART_BUFFER_QUEUE_SUPPORT              false
#define DRV_USART_CLIENTS_NUMBER                    1
#define DRV_USART_SUPPORT_TRANSMIT_DMA              false
#define DRV_USART_SUPPORT_RECEIVE_DMA               false
#define DRV_USART_INSTANCES_NUMBER                  1
#define DRV_USART_PERIPHERAL_ID_IDX0                USART_ID_1
#define DRV_USART_OPER_MODE_IDX0                    DRV_USART_OPERATION_MODE_NORMAL
#define DRV_USART_OPER_MODE_DATA_IDX0               
#define DRV_USART_INIT_FLAG_WAKE_ON_START_IDX0      false
#define DRV_USART_INIT_FLAG_AUTO_BAUD_IDX0          false
#define DRV_USART_INIT_FLAG_STOP_IN_IDLE_IDX0       false
#define DRV_USART_INIT_FLAGS_IDX0                   0
#define DRV_USART_BRG_CLOCK_IDX0                    80000000
#define DRV_USART_BAUD_RATE_IDX0                    921600
#define DRV_USART_LINE_CNTRL_IDX0                   DRV_USART_LINE_CONTROL_8NONE1
#define DRV_USART_HANDSHAKE_MODE_IDX0               DRV_USART_HANDSHAKE_NONE
#define DRV_USART_XMIT_INT_SRC_IDX0                 INT_SOURCE_USART_1_TRANSMIT
#define DRV_USART_RCV_INT_SRC_IDX0                  INT_SOURCE_USART_1_RECEIVE
#define DRV_USART_ERR_INT_SRC_IDX0                  INT_SOURCE_USART_1_ERROR
#define DRV_USART_INT_VECTOR_IDX0                   INT_VECTOR_UART1
#define DRV_USART_INT_PRIORITY_IDX0                 INT_PRIORITY_LEVEL1
#define DRV_USART_INT_SUB_PRIORITY_IDX0             INT_SUBPRIORITY_LEVEL0
#define DRV_USART_POWER_STATE_IDX0                  SYS_MODULE_POWER_RUN_FULL

 

Link to comment
Share on other sites

Screenshot not working.  A message says I can only send 507 KB.  Newbie limit?  I tried reducing the size, but this is a 5K retina screen, so even at 20% of original it's still over the limit.  I compressed the file, but all detail was lost.   Perhaps there's a way to at least allow a meg or two.  Thanks, and cheers!

Link to comment
Share on other sites

You can always upload pictures to our Gallery and then link to them, since you're right, there is a limit for posting pictures directly into a post (I think it's the same limit for everybody).

But thank you for sharing what you found out!

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...