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.

Compiler/TM4C123GH6PM: Controlling FSM with two different timers

Part Number: TM4C123GH6PM

Tool/software: TI C/C++ Compiler

I've been working on this problem for a few weeks now, and I couldn't figure it out.

I need to control five states in a strict time manner. So, I am using two different timers, as showing in the code. Timer0A to wait for 1ms in S1 and S5, and Timer1A to wait for 1us in S2, S3, and S4. The problem is; Timer1A is always off; the wait time should be 1us in S2, S3, and S4 but it always varies between 0.8us and 5us.

Although the wait in Timer1A which control S2, S3, and S4 must be 1us, I changed the Timer1A to be 10us to test if 1us too fast for the microcontroller clock, but I faced another problem which is in every cycle before it switches to Timer0A it waits for 20us instead of 10us. I couldn't stabilize it to be 10us for the S2, S3, and S4.

The questions are, how to control the S2, S3, and S4 so the wait can be 1us before it switches? Is it even passable to use to timers one with 1ms and another for 1us with TM4C123g launchpad? Is there any better way to solve this problem?

I have attached a picture if you want to to see how the FSM should behave.

Thank you all.

#include <stdint.h>
#include "PLL.h"
#include "..//tm4c123gh6pm.h"

#define SW1A (*((volatile unsigned long *)0x40004010)) // PA2 #define SW1B (*((volatile unsigned long *)0x40004040)) // PA4 #define SW2A (*((volatile unsigned long *)0x40004080)) // PA5 #define SW2B (*((volatile unsigned long *)0x40004200)) // PA7 void DisableInterrupts(void); // Disable interrupts void EnableInterrupts(void); // Enable interrupts // State Machines States enum state_machine {State1, State2, State3, State4, State5} ; enum state_machine State = State1; // Variable to control State Machine direction unsigned int complementary_signal = 0; unsigned int count = 0; // Timer0 initialization // This Timer is configured for 1 ms // Calultion for Timer Reload Value // 1ms / 12.5 ns void Timer0_Init() { SYSCTL_RCGCTIMER_R |= 0x01; // 0) activate TIMER0 TIMER0_CTL_R = 0x00000000; // 1) disable TIMER0A during setup TIMER0_CFG_R = 0x00000000; // 2) configure for 32-bit mode TIMER0_TAMR_R = 0x00000002; // 3) configure for periodic mode, default down-count settings TIMER0_TAILR_R = 79000; // 4) reload value TIMER0_TAPR_R = 0; // 5) bus clock resolution TIMER0_ICR_R = 0x00000001; // 6) clear TIMER0A timeout flag TIMER0_IMR_R = 0x00000001; // 7) arm timeout interrupt NVIC_PRI4_R = (NVIC_PRI4_R&0x00FFFFFF)|0x80000000; // 8) priority 4 // interrupts enabled in the main program after all devices initialized // vector number 35, interrupt number 19 NVIC_EN0_R = 1<<19; // 9) enable IRQ 19 in NVIC TIMER0_CTL_R = 0x00000001; // 10) enable TIMER0A } // Timer1 initialization // This Timer is configured for 1 us // Calultion for Timer Reload Value // 1us / 12.5 ns void Timer1_Init(){ SYSCTL_RCGCTIMER_R |= 0x02; // 0) activate TIMER1 TIMER1_CTL_R = 0x00000000; // 1) disable TIMER1A during setup TIMER1_CFG_R = 0x00000000; // 2) configure for 32-bit mode TIMER1_TAMR_R = 0x00000002; // 3) configure for periodic mode, default down-count settings TIMER1_TAILR_R = 79; // 4) reload value TIMER1_TAPR_R = 0; // 5) bus clock resolution TIMER1_ICR_R = 0x00000001; // 6) clear TIMER1A timeout flag TIMER1_IMR_R = 0x00000001; // 7) arm timeout interrupt NVIC_PRI5_R = (NVIC_PRI5_R&0xFFFF00FF)|0x00008000; // 8) priority 4 // interrupts enabled in the main program after all devices initialized // vector number 37, interrupt number 21 NVIC_EN0_R = 1<<21; // 9) enable IRQ 21 in NVIC // Timer1 will be enabled by the interrupt of Timer0 //TIMER1_CTL_R = 0x00000001; // 10) enable TIMER1A } //This timer is only for State1 and State5. It's configured for 1 ms void Timer0A_Handler(void){ TIMER0_ICR_R = TIMER_ICR_TATOCINT;// acknowledge TIMER0A timeout for State1 and State5 if(complementary_signal == 0) { State++; } else { State--; } count = 0; TIMER0_CTL_R = 0x00000000; // disable TIMER0A TIMER1_CTL_R = 0x00000001; // enable TIMER1A } //This timer is only for State2, State3 and State4. It's configured for 1 us void Timer1A_Handler(void){ TIMER1_ICR_R = TIMER_ICR_TATOCINT;// acknowledge TIMER1A timeout count++; if(count == 3) { TIMER1_CTL_R = 0x00000000; // disable TIMER1A TIMER0_CTL_R = 0x00000001; // enable TIMER0A } else { if(complementary_signal == 0) { State++; } else { State--; } } } int main(void){ volatile unsigned long delay; PLL_Init(); // bus clock at 80 MHz SYSCTL_RCGC2_R |= 0x00000001; // 1) activate clock for Port A delay = SYSCTL_RCGC2_R; // allow time for clock to start GPIO_PORTA_AMSEL_R &= ~0xB4; // 3) disable analog on PA2, PA4, PA5 and PA7 GPIO_PORTA_PCTL_R &= ~0xF0FF0F00; // 4) PCTL GPIO on PA2, PA4, PA5 and PA7 GPIO_PORTA_DIR_R |= 0xB4; // 5) PA7 out GPIO_PORTA_AFSEL_R &= ~0xB4; // 6) disable alt funct on PA2, PA4, PA5 and PA7 GPIO_PORTA_DEN_R |= 0xB4; // 7) enable digital I/O on PA2, PA4, PA5 and PA7 Timer0_Init(); // Initialize Timer0 with 1ms period Timer1_Init(); // Initialize Timer1 with 1us period EnableInterrupts(); while(1){ switch(State) { case State1 : SW1A = 0x04; SW1B = 0x10; SW2A = 0x00; SW2B = 0x00; complementary_signal = 0; break; case State2 : SW1A = 0x04; SW1B = 0x10; SW2A = 0x20; SW2B = 0x00; break; case State3 : SW1A = 0x00; SW1B = 0x10; SW2A = 0x20; SW2B = 0x00; break; case State4 : SW1A = 0x00; SW1B = 0x10; SW2A = 0x20; SW2B = 0x80; break; case State5 : SW1A = 0x00; SW1B = 0x00; SW2A = 0x20; SW2B = 0x80; complementary_signal = 1; break; default : SW1A = 0x04; SW1B = 0x10; SW2A = 0x00; SW2B = 0x00; complementary_signal = 0; } } }

  • Hello Mohammed,

    I did not go through your code as it is using DRMs (Direct Register Access Macros), which makes it very hard to debug. Please use TivaWare APIs. for more details of why using TivaWare APIs is better over DRMs, Refer TIP#2 in the following link: e2e.ti.com/.../374640

    Based on the SysClk, a 16-bit Timer might not count 1msec. Use 32-bit Timer instead.

    Thanks,
    Sai
  • May I too - reject the usage of "DRM" in great favor of vendor's terrific API?   Your code development - and all (follow on) diagnostics - both here (unsung helpers) and w/in your firm  (those who must maintain your code) will greatly appreciate the clarity & directness of the API.

    Now - having only glanced at your code (prior to eyes "glazing over") might I suggest:

    • employ timers in one shot mode
    • employ different timers - NOT "A & B" w/on the same timer.  (simplifies - reduces overlap confusion)
    • comply w/vendor Sai's observation re: potential overflow of 16 bit Timer when tasked for mS load
    • Your State diagram duplicates the coding for State 2 & 3 - cannot be right

    Timers are not that difficult - trying to do "too much" and "too close together" confounds & complicates.   Is it not easier to get one Timer to work in the one-shot mode - your test/verify assures that you achieve the correct Timer Duration - and that you may call that timer repeatedly - each time achieving that same duration?   That should serve as Step 1.

    Then using that as a model - dial down a different Timer to (let's say) 100µS - in the same test manner confirm.   Then change it to 10µS - again insuring compliance.   When all proves well you can move to 1µS - but again, test/verify.

    The above is an illustration of KISS - which most always succeeds by breaking the large task into far easier, constituent parts.   Your methodical proof of each best insures that when these individual, tested parts are assembled correctly - the resulting performance will meet your specifications...

  • I am not using the systick, I am using 32-bit timers but thanks tho.

  • This is what I did, I have tested one timer, with simple logic, which is repeatedly switching back and forth between the states with constant switching wait period, and the testing shows that the fastest switching I can do is 10us and that way off what I need which is 1us.

    Do you think the problem is with the way that I have coded it, or I have to change the hardware and go with something faster than 80MHz processor?


    * I have fixed the FSM diagram.
  • One thing to consider, 1uS at 80MHz is only 80 clocks. Even at 1 clock/instruction that's only 80 instructions. How much you have to do in that short interval is going to be quite critical.

    And the reaction time of any peripherals may be an issue as well. For instance if you need to set an output you should probably first have a measure of how quickly you can do that.

    Robert
  • Indeed I see that you've fixed your FSM diagram.

    Now - might it be possible that your, "repeatedly switching back/forth between the states" adds to - thus extends your (apparent) timer delay?
    You might consider a simple bit toggle - upon entry to & exit from - each State. The measure of that bit's "on" time reveals how long you remain in each state. The measure of the exit from an early state (bit resets) to the entry to the next state (bit sets) should reflect the timer's contribution during any particular state. (assumes that your MCU operations w/in the state do not exceed the one-shot Timer's duration.)

    As poster Robert wrote - there's not much to be done in so short a period of time - have you chosen that time duration based upon "real needs?"
  • Mohammed Alqasir1 said:
    Do you think the problem is with the way that I have coded it, or I have to change the hardware and go with something faster than 80MHz processor?

    Some comments:

    1) You don't appear to have posted the code for the PLL_Init(), but have you checked that the CPU frequency has actually been set to 80MHz?

    E.g. by using a pin to output a divided down clock and check the frequency externally.

    2) The outputs are on pins PA2, PA4, PA5 and PA7. i.e. all outputs are on GPIO port A. The existing code has four writes to set the state of each PA2, PA4, PA5 and PA7 pin in turn. The GPIO Data register allows the use of the address mask to change any combination of pins on one GPIO port in a single write.

    Therefore, the code could be optimized to only perform one GPIO Data register write to set all four output pins at once.

  • Chester Gillon said:
    ...address mask (may) change any combination of pins - on (the same) GPIO port - in a single write.

    Indeed that's true - and should enhance code speed.

    Yet - there are State Machines which (deliberately) "disallow" such "single, all in one operations" as they may invalidate vital state sequencing & (necessary) "State to state" time delays.

    A good idea - yet poster must provide the (requested) necessary detail...

  • Yes, if we don't achieve 1us of switching, then we won't have a project. So, mostly we are going to switch hardware and go with a faster clock.
  • Yes, the setup of PLL.Init() is correct and it's at 80MHz because I am using it directly from the library. For the second part of your comment, actually this is what I am doing, I have optimized the code to set all four output pins at once but still didn't help much in term of performance.

    What I am going to do before I switch to another platform, I am going to hard code the 1us and use the timer for 1ms. Hopefully, it will work.

    Thank you for your comments.
  • This is the PLL.C
    // PLL.c // Runs on LM4F120/TM4C123 // A software function to change the bus frequency using the PLL. // Daniel Valvano // September 10, 2013 /* This example accompanies the book "Embedded Systems: Real Time Interfacing to Arm Cortex M Microcontrollers", ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2013 Program 2.10, Figure 2.37 Copyright 2013 by Jonathan W. Valvano, valvano@mail.utexas.edu You may use, edit, run or distribute this file as long as the above copyright notice remains THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. For more information about my classes, my research, and my books, see users.ece.utexas.edu/.../ */ #include "PLL.h" #include "..//tm4c123gh6pm.h" // The #define statement SYSDIV2 in PLL.h // initializes the PLL to the desired frequency. // bus frequency is 400MHz/(SYSDIV2+1) = 400MHz/(7+1) = 50 MHz // see the table at the end of this file #define SYSCTL_RIS_R (*((volatile unsigned long *)0x400FE050)) #define SYSCTL_RIS_PLLLRIS 0x00000040 // PLL Lock Raw Interrupt Status #define SYSCTL_RCC_R (*((volatile unsigned long *)0x400FE060)) #define SYSCTL_RCC_XTAL_M 0x000007C0 // Crystal Value #define SYSCTL_RCC_XTAL_6MHZ 0x000002C0 // 6 MHz Crystal #define SYSCTL_RCC_XTAL_8MHZ 0x00000380 // 8 MHz Crystal #define SYSCTL_RCC_XTAL_16MHZ 0x00000540 // 16 MHz Crystal #define SYSCTL_RCC2_R (*((volatile unsigned long *)0x400FE070)) #define SYSCTL_RCC2_USERCC2 0x80000000 // Use RCC2 #define SYSCTL_RCC2_DIV400 0x40000000 // Divide PLL as 400 MHz vs. 200 // MHz #define SYSCTL_RCC2_SYSDIV2_M 0x1F800000 // System Clock Divisor 2 #define SYSCTL_RCC2_SYSDIV2LSB 0x00400000 // Additional LSB for SYSDIV2 #define SYSCTL_RCC2_PWRDN2 0x00002000 // Power-Down PLL 2 #define SYSCTL_RCC2_BYPASS2 0x00000800 // PLL Bypass 2 #define SYSCTL_RCC2_OSCSRC2_M 0x00000070 // Oscillator Source 2 #define SYSCTL_RCC2_OSCSRC2_MO 0x00000000 // MOSC // configure the system to get its clock from the PLL void PLL_Init(void){ // 0) configure the system to use RCC2 for advanced features // such as 400 MHz PLL and non-integer System Clock Divisor SYSCTL_RCC2_R |= SYSCTL_RCC2_USERCC2; // 1) bypass PLL while initializing SYSCTL_RCC2_R |= SYSCTL_RCC2_BYPASS2; // 2) select the crystal value and oscillator source SYSCTL_RCC_R &= ~SYSCTL_RCC_XTAL_M; // clear XTAL field SYSCTL_RCC_R += SYSCTL_RCC_XTAL_16MHZ;// configure for 16 MHz crystal SYSCTL_RCC2_R &= ~SYSCTL_RCC2_OSCSRC2_M;// clear oscillator source field SYSCTL_RCC2_R += SYSCTL_RCC2_OSCSRC2_MO;// configure for main oscillator source // 3) activate PLL by clearing PWRDN SYSCTL_RCC2_R &= ~SYSCTL_RCC2_PWRDN2; // 4) set the desired system divider and the system divider least significant bit SYSCTL_RCC2_R |= SYSCTL_RCC2_DIV400; // use 400 MHz PLL SYSCTL_RCC2_R = (SYSCTL_RCC2_R&~0x1FC00000) // clear system clock divider field + (SYSDIV2<<22); // configure for 80 MHz clock // 5) wait for the PLL to lock by polling PLLLRIS while((SYSCTL_RIS_R&SYSCTL_RIS_PLLLRIS)==0){}; // 6) enable use of PLL by clearing BYPASS SYSCTL_RCC2_R &= ~SYSCTL_RCC2_BYPASS2; } /* SYSDIV2 Divisor Clock (MHz) 0 1 reserved 1 2 reserved 2 3 reserved 3 4 reserved 4 5 80.000 5 6 66.667 6 7 reserved 7 8 50.000 8 9 44.444 9 10 40.000 10 11 36.364 11 12 33.333 12 13 30.769 13 14 28.571 14 15 26.667 15 16 25.000 16 17 23.529 17 18 22.222 18 19 21.053 19 20 20.000 20 21 19.048 21 22 18.182 22 23 17.391 23 24 16.667 24 25 16.000 25 26 15.385 26 27 14.815 27 28 14.286 28 29 13.793 29 30 13.333 30 31 12.903 31 32 12.500 32 33 12.121 33 34 11.765 34 35 11.429 35 36 11.111 36 37 10.811 37 38 10.526 38 39 10.256 39 40 10.000 40 41 9.756 41 42 9.524 42 43 9.302 43 44 9.091 44 45 8.889 45 46 8.696 46 47 8.511 47 48 8.333 48 49 8.163 49 50 8.000 50 51 7.843 51 52 7.692 52 53 7.547 53 54 7.407 54 55 7.273 55 56 7.143 56 57 7.018 57 58 6.897 58 59 6.780 59 60 6.667 60 61 6.557 61 62 6.452 62 63 6.349 63 64 6.250 64 65 6.154 65 66 6.061 66 67 5.970 67 68 5.882 68 69 5.797 69 70 5.714 70 71 5.634 71 72 5.556 72 73 5.479 73 74 5.405 74 75 5.333 75 76 5.263 76 77 5.195 77 78 5.128 78 79 5.063 79 80 5.000 80 81 4.938 81 82 4.878 82 83 4.819 83 84 4.762 84 85 4.706 85 86 4.651 86 87 4.598 87 88 4.545 88 89 4.494 89 90 4.444 90 91 4.396 91 92 4.348 92 93 4.301 93 94 4.255 94 95 4.211 95 96 4.167 96 97 4.124 97 98 4.082 98 99 4.040 99 100 4.000 100 101 3.960 101 102 3.922 102 103 3.883 103 104 3.846 104 105 3.810 105 106 3.774 106 107 3.738 107 108 3.704 108 109 3.670 109 110 3.636 110 111 3.604 111 112 3.571 112 113 3.540 113 114 3.509 114 115 3.478 115 116 3.448 116 117 3.419 117 118 3.390 118 119 3.361 119 120 3.333 120 121 3.306 121 122 3.279 122 123 3.252 123 124 3.226 124 125 3.200 125 126 3.175 126 127 3.150 127 128 3.125 */

    This is the PLL.h

    // PLL.h
    // Runs on LM4F120/TM4C123
    // A software function to change the bus frequency using the PLL.
    // Daniel Valvano
    // September 10, 2013
    
    /* This example accompanies the book
       "Embedded Systems: Real Time Interfacing to Arm Cortex M Microcontrollers",
       ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2013
       Program 2.10, Figure 2.37
    
     Copyright 2013 by Jonathan W. Valvano, valvano@mail.utexas.edu
        You may use, edit, run or distribute this file
        as long as the above copyright notice remains
     THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
     OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
     VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
     OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
     For more information about my classes, my research, and my books, see
     users.ece.utexas.edu/.../
     */
    
    // The #define statement SYSDIV2 initializes
    // the PLL to the desired frequency.
    #define SYSDIV2 4
    // bus frequency is 400MHz/(SYSDIV2+1) = 400MHz/(4+1) = 80 MHz
    
    // configure the system to get its clock from the PLL
    void PLL_Init(void);
    
    
    /*
    SYSDIV2  Divisor  Clock (MHz)
     0        1       reserved
     1        2       reserved
     2        3       reserved
     3        4       reserved
     4        5       80.000
     5        6       66.667
     6        7       reserved
     7        8       50.000
     8        9       44.444
     9        10      40.000
     10       11      36.364
     11       12      33.333
     12       13      30.769
     13       14      28.571
     14       15      26.667
     15       16      25.000
     16       17      23.529
     17       18      22.222
     18       19      21.053
     19       20      20.000
     20       21      19.048
     21       22      18.182
     22       23      17.391
     23       24      16.667
     24       25      16.000
     25       26      15.385
     26       27      14.815
     27       28      14.286
     28       29      13.793
     29       30      13.333
     30       31      12.903
     31       32      12.500
     32       33      12.121
     33       34      11.765
     34       35      11.429
     35       36      11.111
     36       37      10.811
     37       38      10.526
     38       39      10.256
     39       40      10.000
     40       41      9.756
     41       42      9.524
     42       43      9.302
     43       44      9.091
     44       45      8.889
     45       46      8.696
     46       47      8.511
     47       48      8.333
     48       49      8.163
     49       50      8.000
     50       51      7.843
     51       52      7.692
     52       53      7.547
     53       54      7.407
     54       55      7.273
     55       56      7.143
     56       57      7.018
     57       58      6.897
     58       59      6.780
     59       60      6.667
     60       61      6.557
     61       62      6.452
     62       63      6.349
     63       64      6.250
     64       65      6.154
     65       66      6.061
     66       67      5.970
     67       68      5.882
     68       69      5.797
     69       70      5.714
     70       71      5.634
     71       72      5.556
     72       73      5.479
     73       74      5.405
     74       75      5.333
     75       76      5.263
     76       77      5.195
     77       78      5.128
     78       79      5.063
     79       80      5.000
     80       81      4.938
     81       82      4.878
     82       83      4.819
     83       84      4.762
     84       85      4.706
     85       86      4.651
     86       87      4.598
     87       88      4.545
     88       89      4.494
     89       90      4.444
     90       91      4.396
     91       92      4.348
     92       93      4.301
     93       94      4.255
     94       95      4.211
     95       96      4.167
     96       97      4.124
     97       98      4.082
     98       99      4.040
     99       100     4.000
     100      101     3.960
     101      102     3.922
     102      103     3.883
     103      104     3.846
     104      105     3.810
     105      106     3.774
     106      107     3.738
     107      108     3.704
     108      109     3.670
     109      110     3.636
     110      111     3.604
     111      112     3.571
     112      113     3.540
     113      114     3.509
     114      115     3.478
     115      116     3.448
     116      117     3.419
     117      118     3.390
     118      119     3.361
     119      120     3.333
     120      121     3.306
     121      122     3.279
     122      123     3.252
     123      124     3.226
     124      125     3.200
     125      126     3.175
     126      127     3.150
     127      128     3.125
    */
    

  • Have you actually measured how fast you can toggle a pin? That may be your limiting factor and should be checked before anything else. Just a simple loop toggling the pin. If that is slow then check back, some buses are faster than others and a minor tweak might make a difference.

    Note: Odd though it may seem, it is often true that smaller slower processors can toggle I/O faster than an ARM based micro.

    Robert
  • Mohammed Alqasir1 said:
    I need to control five states in a strict time manner.

    I'm not sure I understood your requirement properly, but do you simply need to control the output of those 4 bits (pins) at a determined time interval?

    Take a look at Luis Afonso's DMA implementation. His strategy is to use a memory array containing the various output combinations required. Then the timer triggers a DMA transfer from the array directly into the pin control registers. It sounds complex, but at the end you will achieve that result with quite a time precision, very little CPU load, and only a few lines of code.

    https://sites.google.com/site/luiselectronicprojects/projects/rgb-matrix/ws2812-dma-ping-pong-mode