This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Advice on debugging a crash

Other Parts Discussed in Thread: TM4C1294NCPDT, SEGGER, CC2650, EK-TM4C1294XL, SYSBIOS

Hi,

I have an RTOS application running on a TM4C1294NCPDT. The code starts up and runs fine. After a while I sometimes get a crash. The likely cause is running off the end of a buffer or similar.

The Debug panel typically shows...

My question is How do I best use the tools (ROV?) to find out what has gone wrong? Is there a way to find out which function in which thread was running at the time? etc

Many thanks,

Richard

  • Hi Richard,

    Debugging should be relatively easy with the ROV tools.

    Here are tips that I found useful while working with TI-RTOS.

    If you get a stack overflow, then the OS will usually get stuck in that same task. You can then see which task is currently active by looking at the Task Module view in ROV. It will give you the start address of the stack and you can then match it with the corresponding task in the ROV detailed view.

    I believe that you can get an error if you get a stack overlow, but I am not familiar enough with CCS to know how it works and how to enable the feature.

    The Task Detailed view will give you the stack peak size for each task which will be helpful when you want to reduce the size of your application. If you suspect a stack overflow and you have enough RAM available, you can boost the size of your tasks' stacks and you can then readjust the sizes once you finished debugging.

    If a heap allocation fails, the malloc call will return a NULL pointer. I would suggest that you have a check after your mallocs (ex: some kind of ASSERT) to make sure that you have successfully allocated the memory.

    If you have a memory corruption problem, you will most likely see an error message that the ROV failed to load.

    The trick part is when you use pointers which could corrupt data without you noticing until your application crashes. The ROV tools will not be helpful in that case, but if you're lucky, the task that caused the memory corruption will remain active and you will be able to see it in the Task Module view.


    Hope this helps

    Michel

  • Hi and thanks for the advice.

    I am still having difficulty with runtime errors. It seems that when I get a crash and the debugger exits, it is the task marked as Preempted that has  gone wrong...

    My questions are...

    What can cause a Preempted Task to fail?

    In the CallStacks panel, what does the "8 <symbol is not available> and the PC is at 0xFFFFFFFFC" actually indicate?

    Thanks

    Richard

  • Hi Richard,

    A pre-empted task means that it has lost control to a higher priority task.

    I am not sure about this one but I believe that a PC at such a value means there was most likely memory corruption (just as you suspected). You can probably do a pause with the debugger to see what the CallStack looks like when it hasn't crashed yet to compare the CallStack. If that is the case, I would start looking at the system stack and then task stacks (USB RX task in this case).

    A memory corruption (if related to RTOS modules) could be caused by a context switch without enough room in the stack to save all the task environment (system stack), or you call a function inside your task which exceeds the size of the task stack (task stack)

    System stack: I don't remember which tab gives you the system stack but try the module tab of the Task ROV view. It could be also be under System. Look through the different views and it shouldn't be too hard to find.

    Task stack: You can look at the detailed tab (of the Task ROV view) to see the stack peaks of each stack.

    If you're still having issues, could you send us screenshots of those other tabs?

    Regards,
    Michel
  • Richard Bland said:
    In the CallStacks panel, what does the "8 <symbol is not available> and the PC is at 0xFFFFFFFFC" actually indicate?

    A PC value with bits[31:5] set to one means a EXC_RETURN (Exception Return) on Cortex-M devices. The value 0xFFFFFFFFC you are seeing isn't one of the valid values for a Cortex-M device as shown on the ARM Cortex-M4 2.3.7. Exception entry and return

    Bit zero clear in the EXC_RETURN value 0xFFFFFFFFC means ARM mode, which isn't valid for a Cortex-M device which only supports THUMB mode instructions. Therefore, suspect the program has crashed in some way leading to a hard fault. The Diagnosing Software Faults in Stellaris® Microcontrollers application note has some guidance on how to check the cause of the fault.

    [While the application note was written to Stellaris devices, it is also applicable to Tiva devices]

  • Thank you both for your replies. I am reasonably familiar with the Stellaris fault diagnosis, but in this case the program ends up in loader_exit() which is I think part of RTOS.

    I could not find a reference to System Stack. The Module tab of ROV gives HWI stack which in my case is set to 4096 and peaks at 964.

    In order to try to prevent Preempting I set all active tasks to the same  priority, but as you see below I still got a task Preempted. How can this happen?

    thanks

    Richard

  • Richard Bland said:
    I am reasonably familiar with the Stellaris fault diagnosis, but in this case the program ends up in loader_exit() which is I think part of RTOS.

    SYS/BIOS installs a fault handler which ends up calling loader_exit() after attempting to report a register dump on the console, which is why I suggested looking at the Cortex-M4 registers which record if any faults had occurred.

  • Richard Bland said:
    I could not find a reference to System Stack. The Module tab of ROV gives HWI stack which in my case is set to 4096 and peaks at 964.

    Sorry, I work with different systems and manufacturers, so my nomenclature gets mixed up. This is the System stack.

    Richard Bland said:
    In order to try to prevent Preempting I set all active tasks to the same  priority, but as you see below I still got a task Preempted. How can this happen?

    Hwis and Swis (they are both considered as tasks in TI-RTOS) have higher priorities. Therefore, they can pre-empt tasks.

    From your screenshots, it seems that the error might possibly be coming from one of your interrupts. Do you have any custom-defined ISRs?

    You can probably see the HWI functions in the Hwi ROV (Detailed and Exception tab) view. Not sure what to look for, but at least it`s a starting point.
    I found this thread that talks about debugging CPU exceptions.

    Regards,

    Michel

  • HWI ROV. Does this look suspicious to you?...

    the Memory at that address is...

    Richard

  • This is a little bit beyond my knowledge in terms of TI-RTOS drivers, but a line of 0's definitely doesn't seem right.

    I believe that you can see where the program hangs by adding the USB driver source files (.c) to your project and compile them with your project (again, I don't know which files those would be but you could do a search for the text usbBusFaultHwiStruct to see where it is used). This will allow to break in the code where the error occurred.

    The drivers are located here:
    C:\ti\<-------tirtos folder------>\packages\ti\drivers

    Michel
  • Michel Solecki said:
    This is a little bit beyond my knowledge in terms of TI-RTOS drivers, but a line of 0's definitely doesn't seem right.

    Looking at tirtos_tivac_2_16_01_14\tirtos_tivac_2_16_01_14_examples\GNU\EK_TM4C1294XL\usbserialdevice\EK_TM4C1294XL.c usbBusFaultHwiStruct is a zero-initialized structure which only initialized with non-zero values when the usbMode is EK_TM4C1294XL_USBHOST.

    i.e. unless the USB is being used in host mode, it is not a problem that usbBusFaultHwiStruct is all zeros.

  • Bit zero clear in the EXC_RETURN value 0xFFFFFFFFC means ARM mode, which isn't valid for a Cortex-M device which only supports THUMB mode instructions. Therefore, suspect the program has crashed in some way leading to a hard fault.

    To test the failure symptoms if a user task attempts to call a function in ARM mode (bit zero of the address cleared) too the SYS/BIOS Task Mutex Example for the TM4C1294NCPDT and modified the task2 function to generate a deliberate exception:

            if (finishCount == 5)
            {
                void (*func_ptr) (xdc_Int);
                System_printf("About to call BIOS_exit from task2 using an invalid function pointer, by clearing bit zero of the address to attempt to enter ARM mode\n");
                System_flush ();
                func_ptr = (void (*)(xdc_Int)) (((unsigned int) BIOS_exit) & ~1);
                (*func_ptr) (0);
            }

    The following CCS screen shot showed what happened when the faulty program was run:

    Points of note are:

    a) The call stack starts with the exception value 0xFFFFFFFC and ends with loaded_exit() being called - which is similar to the call stack which is the subject of the crash.

    b) The task2 SYS/BIOS task which generated the fault is shown with a mode of "Preempted", which is explained by the task2 being the task which was running at the point the exception was raised.

    c) The NVIC_FAULT_STAT "Invalid State Usage Fault" bit is set.

    d) SYS/BIOS has reported a register dump when the exception occurred.

    Using Exception Dump Decoding Using the CCS Register View and entering the PC value of 0x00005d68, SP value of 0x20001408 and LR value of 0x000028d3 into the CCS register view then allowed the stack backtrace to display the point at which the exception occurred:

    Is there a way to find out which function in which thread was running at the time? etc

    If you can get the exception dump from the SYS/BIOS exception handler then you should be able to follow Exception Dump Decoding Using the CCS Register View to find out which function in which the thread was running at the time, along with being able to inspect thread local variables in the thread. However, if the cause of the exception is memory being corrupted / overwritten the variables on the stack may no longer be valid.

  • Hi Chester, thank you very much for  this detailed reply.

    I have to confess to not understanding the code you have written as I have never seen expressions like "void (*func_ptr) (xdc_Int);" or "void (*)(xdc_Int)" or "(*func_ptr) (0);"!

    Anyhow that's not the point as you are demonstrating what happens when a task generates an exception.

    I have just waited 2 hours for my system to crash and this time it reports that the Idle Task was Preempted. I have no code running in the Idle Task, it is simply doing it's normal looking for exited tasks etc.

    Here is a screenshot at this point. In my console window I don't seem to get the register dump that you show. Anyway I wonder if you would give me some guidance as to what to look for...

    thanks

    Richard

  • Richard Bland said:
    I have to confess to not understanding the code you have written as I have never seen expressions like "void (*func_ptr) (xdc_Int);" or "void (*)(xdc_Int)" or "(*func_ptr) (0);"!

    They are C function pointers. Admittedly a rather poor example of their use as I didn't create a typedef for the function pointer.

     

    Richard Bland said:
    Here is a screenshot at this point. In my console window I don't seem to get the register dump that you show. Anyway I wonder if you would give me some guidance as to what to look for...
    Your screen shot does show the end of a register dump in the CIO console, however the start of the register dump has been truncated.

    What is more useful is that your screen shots show that the Hwi -> Exception ROV view has decoded the cause of the exception. The Decoded exception was a Data Access Error for address 0xb8e967. The memory map in the TM4C1294NCPDT datasheet shows that address 0xb8e967 is in a reserved memory region, and so it is understandable that trying to access a reserved address causes an exception.

    The Exception Call Stack in your 2nd screen shot appears to show the exception was caused in the PBM_free function called from the TcpInput function, i.e. within the TI-RTOS network stack. If you paste the value of the SP, LR and PC registers shown in the ROV Exception Context into the CCS register view, as per Exception Dump Decoding Using the CCS Register View, that should allow you to investigate the state of the code when the exception occurred.

    Not sure if the root cause of the exception is a bug in the TI-RTOS network stack, or something in your application which overwrites memory. Given that you said the exception in the above screen shots took 2 hours to occur guess that is not easily repeatable to investigate.

    [Until I saw your screen shot I didn't realize that the Exception ROV view existed, so thanks for teaching me something.]

  • Hi Chester,

    I have been working on this extensively and am at a loss as to what to do next.

    I did find some include references to Tivaware 2.1.2.111 which is not the version supplied with RTOS, so I fixed that - screenshot of my project and includes - perhaps you can spot something wrong here...

    I also increased the packet size for TCP to 1460 and the number of reconstruction packets to 4. Checking the ethernet flow using Wireshark reveals no errors.

    However after a certain time (varies from a few minutes to a few hours) I get this fault. It appears it is always in tcpin.c (once it was in pbm.c) both of which are files in the NDK stack. Here is a screenshot of the plcae I end up...

    Any suggestion of what to do next would be most appreciated as I have a huge project resting on this and I'm under considerable pressure from above to make this work!

    Thanks,

    Richard

  • Your 2nd screenshot shows a PC of 0xBEBEBEBE in your Exception call stack.  SYS/BIOS Tasks have their stacks initialized to 0xBEBEBEBE.  Perhaps you have a local function pointer variable that is not properly initialized?

    Can you check your Tasks' stack usage?  You can do so by clicking the Task module in your ROV window.  There will be a "stack used" or similar field.

    You can also click the BIOS module in ROV and then click the "Scan for errors" tab, this might show something.

    Regards,

    - Rob

  • Richard Bland said:
    However after a certain time (varies from a few minutes to a few hours) I get this fault. It appears it is always in tcpin.c (once it was in pbm.c) both of which are files in the NDK stack. Here is a screenshot of the plcae I end up...

    Does the fault always occur on the same address / source line in tcpin.c, or does the address / source line vary?

    The exception call stack shows the source file line with the PC address.

    Are you able to share a project which demonstrates the issue?

  • Hi Chester and Rob, thank you for your input here.

    I haven't used any function pointers in the code I have written.

    I have checked the stack usage in ROV/Task/Detailed and ensured that all stackSize are at least twice as big as stackPeak. Unfortunately after the exception this tab in ROV does not load, it reports "the project may be corrupted".

    ROV/BIOS/Scan for Errors simply says "An exception occurred".

    The fault is not always in the same place in tcpin.c but it is always somewhere in TcpInput(), (except once when it was in pbm.c).

    I am working on a version of this project which can run on the 1294 launchpad, but don't know yet if it will demonstrate the fault. If it does I will get it to you somehow.

    Best regards,
    Richard
  • Richard Bland said:
    The fault is not always in the same place in tcpin.c but it is always somewhere in TcpInput(), (except once when it was in pbm.c).

    Given those symptoms not sure what the cause of the problem is, and could be a bug in either the TI-RTOS network stack or something in the application writing to a memory area it shouldn't.

    If your debug probe supports SWO Trace then maybe the Statistical Function Profiling or Interrupt Analyzerin the CCS Trace Analyzer might highlight the conditions which lead to the exception (Trace Analyzer User’s Guide).

    Also, are you able to increase the frequency of the Ethernet messages, as that might might make the failure happen more quickly?

  • Hi Chester, thanks for your help here.

    I am simply using a Stellaris ICDI from a launchpad board, so I imagine the tracing you describe is not possible. I am very happy to purchase a more sophisticated debug probe to help me find this issue. Do you have any advice as to what to get?

    The ethernet TCP payloads are being received at a constant ~50Kbytes/second so it's fairly busy anyway.

    I am not clear on whether I need to use fdShare(). My project has grown out of the TcpEcho example. TcpHandler() accepts a client socket and creates two tasks that use the same clientfd. One task blocks on recv() and the other blocks on a semaphore which is posted when there is data to send(). Both tasks have the same priority. The project compiles and runs quite happily whether or not I put fdShare ((SOCKET(clientfd)) before the second use of clientfd. To be honest I'm not clear where fdShare() should be used - the NDK documentation says to use fdOpenSession() but this is not mentioned in the examples. Just looking for things that may cause my problem.

    Best regards,
    Richard
  • Richard Bland said:
    I am simply using a Stellaris ICDI from a launchpad board, so I imagine the tracing you describe is not possible. I am very happy to purchase a more sophisticated debug probe to help me find this issue. Do you have any advice as to what to get?

    The Stellaris ICDI doesn't support SWO trace.

    The XDS200 is a standalone debug probe which supports SWO trace, although I haven't got a XDS200 to test that.

    The Segger J-Link debug probe hardware supports SWO trace. However as of CCS 6.2.0 and J-Link support 6.10.3 I wasn't able to get the SWO trace on a J-Link to work with a TM4C1294NCPDT due to CCS reporting a "The specified breakpoint type 'Trace' does not exist on this target" when attempted to use the hardware trace analyzer.

    The XDS110 debug probe supports SWO trace. There is not yet a standalone XDS110 debug probe, although I was able to use the "External Target" connector on a CC2650 Launchpad to use the XDS110 on the CC2650 Launchpad to debug a TM4C1294NCPDT and use SWO trace to collect profiling information.

    Richard Bland said:
    I am not clear on whether I need to use fdShare().

    I will try and look into that.

  • Richard Bland said:
    To be honest I'm not clear where fdShare() should be used - the NDK documentation says to use fdOpenSession() but this is not mentioned in the examples. Just looking for things that may cause my problem.

    Looking at tirtos_tivac_2_16_01_14\products\ndk_2_25_00_09\packages\ti\ndk\tools\console\contest.c shows an example of two tasks TcpShareRX and TcpShareTX sharing the same socket. Both the TcpShareRX and TcpShareTX tasks call fdOpenSession( TaskSelf() ) and fdShare(s), where s is the shared socket. The ShareTest function which creates the TcpShareRX and TcpShareTX tasks passes the shared socket s as an input parameter to the tasks.

    I found the contest.c example referenced from the thread Concerto NDK , socket open/close management between shared tasks which has the comment:

    If you are sharing a socket from a parent task, you need add a fdShare() immediately after the fdOpenSession().

  • Richard Bland said:
    Just looking for things that may cause my problem.

    I remember that there was a problem found in tirtos_tivac_2_16_00_08 where the Ethernet MAC driver for the TM4C129 devices didn't allow the maximum size Ethernet packet to be received - see TivaC NDK TCP: Can't receive large packets (>=1460 bytes)

    During the investigation into the problem of not being able to receive the maximum size Ethernet packet found that the driver may also allow "undefined behavior" due to the size of the Ethernet receive descriptors not being a multiple of 4 bytes.

    The problem is still present in tirtos_tivac_2_16_01_14. The referenced thread contains a modified EMACSnow.c with a fix. Might be worth using the fixed driver, rather than having to re-compile TI-RTOS you can just add the modified EMACSnow.c to your project.

  • My project has grown out of the TcpEcho example. TcpHandler() accepts a client socket and creates two tasks that use the same clientfd. One task blocks on recv() and the other blocks on a semaphore which is posted when there is data to send(). Both tasks have the same priority.

    I have been trying two versions of the TcpEcho example on a EK-TM4C1294XL. In both examples:

    - The Tcp.transmitBufSize and Tcp.receiveBufSize have been increased from the default 1024 to 10240 bytes.

    - The buffer used in send() and recv() calls has been increased from the default 256 to 1460 bytes (maximum TCP data payload for non-jumbo Ethernet packet).

    - Used the modified EMACSnow.c which allows the maximum size Ethernet packet to be received.

    The differences are:

    a) One example uses a single task to receive and re-transmit on the socket.

    b) The other example used a pair of transmit and receive tasks which share the same socket, where a mailbox is used to pass the receive buffers to the transmit task.

    To try and stress the test I started four instances of the tirtos_tivac_2_16_01_14\packages\examples\tools>tcpSendReceive program from a PC:

    tcpSendReceive 192.168.0.4 1000 1 -l4380 -s0
    tcpSendReceive 192.168.0.4 1000 2 -l4380 -s0
    tcpSendReceive 192.168.0.4 1000 3 -l4380 -s0
    tcpSendReceive 192.168.0.4 1000 1 -l5000 -s0

    The example which uses a single task per socket to receive and transmit passes data reliably.

    Whereas the example which uses a pair of transmit and receive tasks can fail in a mater of seconds when use the above combination of the tcpSendReceive programs. The failure symptoms are that:

    - The tcpSendReceive exit reporting "stopping test. recv returned -1"

    - Wireshark shows the EK-TM4C1294XL stop outputting any Ethernet packets

    - The TM4C1294NCPDT doesn't suffer any exceptions, but when halt in the debugger the ROV view shows all tasks except the ti.sysbios.knl.Task.IdleTask task are blocked.

    Not sure if there is a bug in my example which uses a pair of transmit and receive tasks per socket, or if have found an issue in the TI-RTOS NDK.

    Example using one task per socket : 3034.tcpEcho_EK_TM4C1294XL_TI_TivaTM4C1294NCPDT.zip

    Example using a pair of receive and transmit tasks per socket : 2248.tcpEcho_rx_and_tx_tasks.zip

  • Hi Chester,

    Working over the weekend - thank you very much for your attention - much appreciated :)

    I have an XDS200 arriving today hopefully.

    fdShare(). I discovered that fdOpenSession() happens automatically due to this line in the .cfg file

    /* automatically call fdOpen/CloseSession for our sockets Task */

    Global.autoOpenCloseFD = true;

    It doesn't seem to make any difference whether or not I put fdShare() at the beginning of each child task, the sockets all open and close cleanly.

    I had a look at contest.c (how come these examples get so deeply buried?) but it looks like it is using the "raw" API so I wasn't sure how relevant this is to my setup.

    I have added the modified EMACSnow.c to my project. We are writing the client code too and have limited it to a max packet size of 1460.

    Also I have set...

    Tcp.transmitBufSize = 1460;

    Tcp.receiveBufSize = 1460;

    Tcp.receiveBufLimit = 5840;

    Tcp.maxNumReasmPkts = 4;

    Unfortunately makes no difference to the system crash.

    Thank you for your time in writing test codes.  My tx and rx tasks are almost indentical to yours - I can't figure out how to put a link to a zip file here like you did, otherwise I'd put them here!

    It seems perhaps I should put all uses of a socket in one task - might be quicker than trying to find out why this goes wrong.

    Thanks and best regards

    Richard

  • I wrote a single task that handles both rx and tx using a non-blocking socket, but I still have the same fault :(

    Apparently CCS does not support Trace for Cortex M devices...
    e2e.ti.com/.../1851431

    So is it true that I can't use my new XDS200 to trace backwards to find out where the error is happening in my TM4C1294?
    If so, it's no wonder I am not understanding what's going on! Argh!

    What is the advantage of the $250 XDS200 that needs a $450 CCS licence over the free ICDI? There has to be some reason for spending that much!

    Richard
  • Richard Bland said:
    Apparently CCS does not support Trace for Cortex M devices...
    e2e.ti.com/.../1851431

    So is it true that I can't use my new XDS200 to trace backwards to find out where the error is happening in my TM4C1294?

    The thread you referenced says that the Embedded Trace Macrocell (ETM) feature is not supported by CCS 6 for Cortex-M devices.

    What is supported for Cortex-M devices by CCS 6 is ITM/DWT tracing via the single wire output (SWO) pin.

    Compared to the ETM, the ITM/DWT tracing is lower bandwidth. The ITM/DWT tracing doesn't allow a trace of every instruction, but samples the Program Counter at regular intervals.

    Richard Bland said:
    What is the advantage of the $250 XDS200 that needs a $450 CCS licence over the free ICDI?

    Compared to the ICDI, the XDS200 supports more devices, faster download speed and ITM/DWT tracing. As for the licence cost, there are currently promotions to get a CCS licence for the price of a launchpad - see https://store.ti.com/Search.aspx?k=promo&pt=-1 

    Richard Bland said:
    I wrote a single task that handles both rx and tx using a non-blocking socket, but I still have the same fault :(

    Can you run a Wireshark capture on the Ethernet traffic prior to the fault to see if there is any evidence of problems prior to the fault on the Tiva device? E.g. do the socket window sizes reduce suggesting the Tiva device isn't keeping up with the received data?

  • Hi Chester,

    Thank you for that. I have found the download speed to the XDS200 to be faster, and thanks for pointing me at the promo - I hope it is valid in the UK!

    I have used the old-fashioned technique of removing parts of the program until the problem goes away - rather a time-consuming process when it only fails a few times a day! It looks like it's using the ADCs in a thread that's causing my issues. The ADCs do not have a specific RTOS driver whereas all the other peripherals do (why?) so I have used the Tivaware drivers (ADCs are monitoring supply volts and core temperature).

    The code below sets up the ADC hardware and starts a task. The task wakes up every 100mS and inspects a pin called sampleRatesReady. This pin is set by my fpga once a second and the act of reading the sample rates (via the EPI) clears the pin. If I comment-out getTemperature() and getVolts() my program does not crash (so far!) as described in the posts above.

    Can anyone point out what's going wrong here and how to avoid it please?

    Many thanks for the support,
    Richard


    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>

    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_gpio.h"
    #include "inc/hw_adc.h"

    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/adc.h"

    #include <xdc/std.h>
    #include <xdc/runtime/Error.h>
    #include <xdc/runtime/System.h>

    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>

    #include "VulcanTiva.h"
    #include "usb.h"
    #include "epi.h"

    //..................................................................
    #define OFFSET 147 //from data sheet
    #define MULTIPLIER 247 //75*3.3 3.3V ref
    #define DIFF 8 //4096/MULTIPLIER/2
    uint32_t raw_temp, last_raw_temp;
    uint8_t temperature, last_temperature;

    //..................................................................
    #define SCALE_A 124 //4096/33 3.3V ref
    #define SCALE_B 62 //4096/33/2
    uint32_t ADC0_raw[8];
    uint32_t last_ADC0_raw[8];
    uint8_t volts[7];
    uint8_t last_volts[7];
    uint8_t vmaxs[7] = {11, 12, 16, 19, 26, 34, 51};
    uint8_t vmins[7] = { 9, 11, 14, 17, 24, 32, 49};

    //------------------------------------------------------------------
    uint8_t getTemperature(void)
    {
    uint32_t ADC1_value[4];
    uint8_t chng;

    ADCProcessorTrigger(ADC1_BASE, 2); //trigger
    while(ADCIntStatus(ADC1_BASE, 2, false)) //wait complete
    {
    }
    ADCIntClear(ADC1_BASE, 2); //clear the ADC interrupt flag
    ADCSequenceDataGet(ADC1_BASE, 2, ADC1_value); //read ADC values

    raw_temp = ADC1_value[3];

    temperature = (uint8_t)(OFFSET - ((MULTIPLIER * raw_temp) >> 12));

    if(temperature != last_temperature) {
    last_temperature = temperature;
    chng = 1;
    }

    return chng;
    }

    //------------------------------------------------------------------
    uint8_t getVolts(void)
    {
    uint8_t chng, i, verr;
    int32_t diff1, diff2;

    ADCProcessorTrigger(ADC0_BASE, 0); //trigger
    while(ADCIntStatus(ADC0_BASE, 0, false)) //wait complete
    {
    }
    ADCIntClear(ADC0_BASE, 0); //clear the ADC interrupt flag
    ADCSequenceDataGet(ADC0_BASE, 0, ADC0_raw); //read ADC values

    for(i=0;i<5;i++) {
    diff1 = ADC0_raw[i] - last_ADC0_raw[i];
    diff2 = last_ADC0_raw[i] - ADC0_raw[i];
    if((diff1 > (SCALE_A/2)) | (diff2 > (SCALE_A/2)))
    volts[i] = (uint8_t)(ADC0_raw[i] / (SCALE_A)); //only resolves 0.1V - is this good enough?
    }
    for(i=5;i<7;i++) {
    diff1 = ADC0_raw[i] - last_ADC0_raw[i];
    diff2 = last_ADC0_raw[i] - ADC0_raw[i];
    if((diff1 > (SCALE_B/2)) | (diff2 > (SCALE_B/2)))
    volts[i] = (uint8_t)(ADC0_raw[i] / (SCALE_B));
    }
    for(i=0;i<7;i++) {
    last_ADC0_raw[i] = ADC0_raw[i];
    }

    verr = 0;
    for(i=0;i<7;i++) { //check for voltage errors
    if(volts[i] > vmaxs[i])
    verr = 1;
    if(volts[i] < vmins[i])
    verr = 1;
    }
    if(verr == 1) {
    voltLEDon;
    }
    else {
    voltLEDoff;
    }

    chng = 0;
    for(i=0;i<7;i++) {
    if(last_volts[i] != volts[i]) {
    chng = 1;
    last_volts[i] = volts[i];
    }
    }

    return chng;
    }

    //------------------------------------------------------------------
    void pollTasks(void)
    {
    while(1) {
    Task_sleep(100);

    //...........................................................................................
    if(sampleRatesReady) { //set every second
    tivaLED0tog; //indicate running

    if(readSampleRates() == 1) //sample rates...
    txSampleRates();

    //if(getTemperature() == 1) //temperature...
    // txTemperature();

    //if(getVolts() == 1) //volts...
    // txVoltages();
    }
    }
    }

    //------------------------------------------------------------------
    void initPollTask(void)
    {
    Task_Params taskParams;
    Task_Handle pollTask;
    Error_Block eb;

    Error_init(&eb);
    Task_Params_init(&taskParams);
    taskParams.stackSize = 512;
    taskParams.priority = 2;

    pollTask = Task_create((Task_FuncPtr)pollTasks, &taskParams, &eb);
    if (pollTask == NULL) {
    System_printf("Error: Failed to create poll_task\n");
    System_flush();
    }
    }

    //-----------------------------------------------------------
    void initPsuMon(void)
    {
    uint32_t i;

    //ADC0...............................................................................................................................
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0))); //wait for peripheral ready

    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_0); //1V
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_1); //1.2V
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_2); //1.5V
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_3); //1.8V
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_4); //2.5V
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_5); //3.3V
    MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_6); //5V

    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1); //clock for both adcs

    // ADCReferenceSet(ADC0_BASE, ADC_REF_EXT_3V);
    ADCReferenceSet(ADC0_BASE, ADC_REF_INT);
    ADCSequenceDisable(ADC0_BASE, 0); //disable sequence before we change it
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0); //select processor (software) trigger
    ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH15);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH14);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH13);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH12);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_CH7);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_CH6);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_CH5);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_CH5 | ADC_CTL_END);
    ADCIntClear(ADC0_BASE, 0); //clear the interrupt status flag
    ADCSequenceEnable(ADC0_BASE, 0); //enable sequence

    //ADC1...............................................................................................................................
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
    while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1))); //wait for peripheral ready
    //need to discard first 2 or 3 samples of temp sensor - errata
    // ADCReferenceSet(ADC1_BASE, ADC_REF_EXT_3V);
    ADCReferenceSet(ADC1_BASE, ADC_REF_INT);
    ADCSequenceDisable(ADC1_BASE, 2); //disable sequence before we change it
    ADCSequenceConfigure(ADC1_BASE, 2, ADC_TRIGGER_PROCESSOR, 1); //select processor (software) trigger
    //HWREG(ADC_O_SSTSH2) = 0x00004444; //set sample and hold to 16 clocks
    ADCSequenceStepConfigure(ADC1_BASE, 2, 0, ADC_CTL_TS); //
    ADCSequenceStepConfigure(ADC1_BASE, 2, 1, ADC_CTL_TS); //
    ADCSequenceStepConfigure(ADC1_BASE, 2, 2, ADC_CTL_TS); //
    ADCSequenceStepConfigure(ADC1_BASE, 2, 3, ADC_CTL_TS | ADC_CTL_END); //
    ADCIntClear(ADC1_BASE, 2); //clear the interrupt status flag
    ADCSequenceEnable(ADC1_BASE, 2); //enable sequence

    for(i=0;i<7;i++) {
    last_volts[i] = 0;
    volts[i] = 0;
    }

    temperature = 0;

    initPollTask();
    }
  • Why does it remove all the tabs?
    Sorry, it makes the code much harder to read.
    Is there a better way to post code or attach a file?

    Richard
  • Further to this - it seems the problem is with the temperature measurement.

    I have initialised ADC1 to read the temperature like this...

    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
    while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1))); //wait for peripheral ready
    ADCReferenceSet(ADC1_BASE, ADC_REF_INT);
    ADCSequenceDisable(ADC1_BASE, 2); //disable sequence before we change it
    ADCSequenceConfigure(ADC1_BASE, 2, ADC_TRIGGER_PROCESSOR, 1); //select processor (software) trigger
    ADCSequenceStepConfigure(ADC1_BASE, 2, 0, ADC_CTL_TS); //
    ADCSequenceStepConfigure(ADC1_BASE, 2, 1, ADC_CTL_TS); //
    ADCSequenceStepConfigure(ADC1_BASE, 2, 2, ADC_CTL_TS); //
    ADCSequenceStepConfigure(ADC1_BASE, 2, 3, ADC_CTL_TS | ADC_CTL_END); //
    ADCIntClear(ADC1_BASE, 2); //clear the interrupt status flag
    ADCSequenceEnable(ADC1_BASE, 2); //enable sequence

    and read the temperature like this...

    uint32_t ADC1_value[4];

    ADCProcessorTrigger(ADC1_BASE, 2); //trigger
    while(ADCIntStatus(ADC1_BASE, 2, false)) //wait complete
    {
    }
    ADCIntClear(ADC1_BASE, 2); //clear the ADC interrupt flag
    ADCSequenceDataGet(ADC1_BASE, 2, ADC1_value); //read ADC values
    raw_temp = ADC1_value[3]; //must discard first 2 or 3 readings - errata

    the data sheet says sequence 2 reads 4 adc samples into a fifo of depth 4, so one would expect to get 4 values returned by ADCSequenceDataGet() hence my array ADC1_value[4].

    This code crashes (and I also get unreliable temperature readings).
    However if I increase the size of ADC1_value to ADC1_value[8] it does not crash.
    Have I missed an errata about this?

    Best regards
    Richard
  • However if I increase the size of ADC1_value to ADC1_value[8] it does not crash.

    Looking at the source code of ADCSequenceDataGet() inside the TivaWare driverlib\adc.c source file, ADCSequenceDataGet reads samples from the ADC FIFO until the FIFO is empty. The maximum number of samples which ADCSequenceDataGet can return is 8. Therefore, to avoid the possibility of ADCSequenceDataGet overwriting the pui32Buffer argument it makes sense for the pui32Buffer argument to point to an array of 8 elements. ADCSequenceDataGet also returns the number of samples stored which the calling code could check.

    I took your example code and placed it in a standalone project using the TivaWare 2.1.1.71b within tirtos_tivac_2_16_01_14 :

    #include <stdint.h>
    #include <stdbool.h>
    #include <assert.h>
    #include <string.h>
    
    #include <hw_memmap.h>
    #include <sysctl.h>
    #include <adc.h>
    
    int main(void) {
        uint32_t ADC1_value[8]; /* The maximum number of samples which can be read from the ADC FIFO by ADCSequenceDataGet */
        volatile uint32_t raw_temp;
        int32_t num_samples;
    
        memset (ADC1_value, 0xee, sizeof (ADC1_value));
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
        while(!(SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1)));    //wait for peripheral ready
        ADCReferenceSet(ADC1_BASE, ADC_REF_INT);
        ADCSequenceDisable(ADC1_BASE, 2); //disable sequence before we change it
        ADCSequenceConfigure(ADC1_BASE, 2, ADC_TRIGGER_PROCESSOR, 1); //select processor (software) trigger
        ADCSequenceStepConfigure(ADC1_BASE, 2, 0, ADC_CTL_TS); //
        ADCSequenceStepConfigure(ADC1_BASE, 2, 1, ADC_CTL_TS); //
        ADCSequenceStepConfigure(ADC1_BASE, 2, 2, ADC_CTL_TS); //
        ADCSequenceStepConfigure(ADC1_BASE, 2, 3, ADC_CTL_TS | ADC_CTL_END);    //
        ADCIntClear(ADC1_BASE, 2); //clear the interrupt status flag
        ADCSequenceEnable(ADC1_BASE, 2); //enable sequence
    
        for (;;)
        {
            ADCProcessorTrigger(ADC1_BASE, 2); //trigger
            while(ADCIntStatus(ADC1_BASE, 2, false)) //wait complete
            {
            }
            ADCIntClear(ADC1_BASE, 2); //clear the ADC interrupt flag
            num_samples = ADCSequenceDataGet(ADC1_BASE, 2, ADC1_value); //read ADC values
            assert (num_samples == 4);
            raw_temp = ADC1_value[3]; //must discard first 2 or 3 readings - errata
        }
    
        return 0;
    }
    

    With this example I can't repeat the problem of ADCSequenceDataGet returning 8 samples since:

    a) The assertion check on the return value from ADCSequenceDataGet doesn't fail, meaning 4 samples have been returned.

    b) When halt in the debugger the raw_temp value is a reasonable room temperature value, and only four values have been stored in the ADC1_value array (since array elements 4 to 7 still have their initial values).

    The complete example project is attached 8204.TM4C1294_adc_sequence.zip

  • Hi Chester,

    I have now had a chance to investigate this further.

    I did the same as you and  watched the number of samples returned by ADCSequenceDataGet().

    I find I can get 5, 6, 7 or 8 samples returned. Here is the test code and the resultant array of results...

    Therefore I would highly recommend that all ADC sequencers are always given an array[8] to put their results in!

    Best regards and many thanks for your support and patience with this issue,

    Richard

  • Ha - found the problem - there is a "!" missing in the while(ADCIntStatus(..))
    It should be while(!ADCIntStatus(..))
    So the "wait complete" drops straight through and we read the fifo before it is ready.
    Why this sometimes makes it return too many values I don't know.

    Amazing how difficult a simple typo is to find sometimes - that development for you!
    Richard