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.

TM4C123, two timers at the same time !!

I am using TivaC TM4C123 MCU. I am trying to send data serially through one pin and a synchronized clock through another pin. Here is an illustration:

I used Timer0 module for the serial data and Timer1 for the clock. The frequency of the clock should be twice the data. Here is the important part of the code:

void DATA(long number, int iteration){ //number: data to be send serially
long x=0;                           // iteration: number of bits to be send 
for(int y=0; y<iteration ; y++){
  while((TIMER0->RIS & 0x00000001) != 1){}     //wait until Timer0 times out 
   x= number & (0x1<<y);     //Get each bit individually  
  if(x==(0X1<<y))     //If bit is 1
     GPIOF->DATA |= (1<<1);   //Make PF1 High 
  else if(x==0X0) //If bit is 0
     GPIOF->DATA &= 0XFD;   //Make PF1 Low
     TIMER0->ICR |= (1<<0);     //Reset Timer0 flag
  while((TIMER1->RIS & 0x00000001) != 1){}     //wait until Timer1 times out
      GPIOF->DATA ^= (1<<2);    //Toggle clock
      TIMER1->ICR |= (1<<0);    //Reset Timer1 flag
   }
  }

   int main(){
     GPIO_INIT();   // Initiate GPIO
     TIMER_INIT();  // Initiate Timers with Timer0= 1/2 Timer1
     while (1){
     DATA(0XAB01,16); // 0xAB01 16 bit data need to be send serially 
       }
    }

There is a problem in this code, which is both of the data and the clock have the same frequency, which means that Timer0 or Timer1 are disabled.

To make my problem more clear and easy to understand. I wrote two simple codes with a graph for each of them. Here is the first code: I am using While loop

  int main()
   {
    GPIO_INIT();  
    TIMER_INIT();   //Timer0= 1/2 Timer1
    while (1){
     while((TIMER0->RIS & 0x00000001) != 1){}   //Wait for TIMER0 to time out
     GPIOF->DATA ^= (1<<1);   //Toggle PF1
     TIMER0->ICR |= (1<<0);   //Reset TIMER0 flag

     while((TIMER1->RIS & 0x00000001) != 1){}    //Wait for TIMER1 to time out
     GPIOF->DATA ^= (1<<2);    // Toggle PF2
     TIMER1->ICR |= (1<<0);    //Reset TIMER1 flag
      }
        }

Here is the output I got:

Here is the 2nd code: I am using If condition

int main(){

GPIO_INIT();
TIMER_INIT();    //Timer0= 1/2 Timer1
while (1){
if((TIMER0->RIS & 0x00000001) == 1){    //If TIMER0 out timed out 
GPIOF->DATA ^= (1<<1);    //Toggle PF1
TIMER0->ICR |= (1<<0);   //Reset TIMER0 flag
   }
       
  if((TIMER1->RIS & 0x00000001) == 1){   //If TIMER1 timed out
  GPIOF->DATA ^= (1<<2); //Toggle PF2
  TIMER1->ICR |= (1<<0); //Reset TIMER1 flag
     }
       } }

Here is the output:

Conclusion: When I used the if condition the two timers are working normally, but when I used While loop only one timer is working and the other timer is just using the same counts of the other. 

  • Abdelrahman Tarief said:
    If my explanation of the problem is not clear, let me know.

    May I commend you for an effective explanation - which includes the (always) helpful, Signal Timing Chart.

    That said - I believe there are 2 "weaknesses" w/in your method of attack:

    • You've employed the dreaded "DRM" style of coding - which greatly increases the time & effort required by those seeking to assist.   This vendor provides a far more effective code method w/in its (famed) API.   The API is (again), "Tried, True, Tested" - none of that extends to DRM code - which proves laborious (for users) to create - a nightmare (for helpers) to troubleshoot.   This vendor recognizes this - encourages users to, "Use the API."  Nothing prevents you from using the API AND digging into the Register utilization - as well.
    • Your use of 2 Timer "Halfs" - w/in the same (single) Timer - subjects you (once more) to additional complexity - does it not?   (can we note, "Glutton for punishment?"  ARM MCUs are (very) rich in Timers - employing two different Timers should quickly/easily resolve your issue.  The desire for Peripheral "optimization" should not trump the success of your mission!

    Some here - especially those in the Tech Biz - employ "KISS" to develop quickly, accurately, & w/the greatest chance of success.   DRM & use of both Timers w/in a single Timer Module are "anti-KISS"  and both are central w/in your posting...  Would not "simplification" assist - both here - and (long) into the future?   Few (points) attach to those choosing the (long & hard) path - when superior ones beckon...

  • First of all, thanks for your reply.
    By API, do you mean Tiva ware ? I am going to ask a trivial question, what does DRM mean exactly ?
    For the timer modules, aren't Timer0 and Timer1 separated ? If they are not, what are two stand alone timers example ?
    Thanks in advance.
  • Thank you, as well.

    Indeed - the API includes the "Peripheral Driver Lib User Guide" and hundreds of tried/tested code examples - neatly (and effectively) organized by Peripheral.

    DRM (Direct Register Manipulation/Management/Macro). Entirely too "bit oriented" and IMPOSSIBLE to understand or analyze w/out constant, chronic MCU manual "hunt, find, probe each/every MCU Register in play. This is the code you present. And is "all we had" back in the dark-ages - but vastly untrue, today!

    If the Timers w/in a (single) timer are separated - that's done in a manner which (thus far) has over-challenged you - has it not? Thus my KISS suggestion - SIMPLIFY!

    You seek Two stand alone timer examples - is that (really) necessary? You're clearly smart enough to "change the Timer number" after succeeding w/"just one." (simple copy/paste (after) you get the first timer to work - and change (only) the timer number and name/create the 2nd timer's interrupt handler.

    Allez mon ami - and do think (and use) KISS...
  • It seems that the problem is with the code it self not with anything else. I have updated the question, if you can check the difference between the two codes I will be grateful.
  • Hi,

    If you split a 32bit timer into two sub-timers, each sub-timer is now 16bits long, so you cannot fill up them with huge constants like 8000000 - you changed the initial posted code - this is I remember in your case.

    Use API configuration function for that, and maybe explain what you try to do with your function,  at this stage we cannot comment about.

  • Greetings Petrei,

    Poster's reluctance to employ KISS (of course - never explained nor justified) leads (again) to pain/suffering, on-going delay!
  • Greetings, cb1

    We should note more and more posters trying to use CMSIS package - as this is already introduced for MSP432 - maybe soon will be available such for Tiva with another libraries.

    Regards,

  • Second cb1's suggestion to convert to TivaWare

    Also

    Abdelrahman Tarief said:
    GPIOF->DATA |= (1<<1);

    Using structures and unions to interface with hardware is generally considered a bad idea.

    Using bitfields to interface with hardware is generally considered a very, very bad idea.

    Robert

  • Robert Adsett said:
    Using structures and unions to interface with hardware is generally considered a bad idea.

    Using bitfields to interface with hardware is generally considered a very, very bad idea.

    The ARM Cortex Microcontroller Software Interface Standard (CMSIS) specifically uses header files with structures, unions and bitfields to overall hardware peripherals - see the examples in https://www.keil.com/pack/doc/CMSIS/SVD/html/svd__s_v_d_conv_pg.html. Note that the peripheral header files can be auto-generated from a schema, and that the generation program has the option of creating bitfield structures which are MISRA compliant, but not CMSIS compliant.

    I am curious to know why you consider such interfaces to hardware a bad idea. 

  • Chester Gillon said:
    I am curious to know why you consider such interfaces to hardware a bad idea. 

    Basically it falls back to the language definition. Using structures is very tempting (especially to beginners) but the result of doing so depends on the compiler and sometimes even the compiler revision. More subtly sometimes it may depend on the silicon.

    The language guarantees that the members of a struct will be allocated in order.

    It does not guarantee

    • the size of the struct
    • The alignment of the members of the struct
    • How the members will be accessed

    The second and third have the biggest effect. Generally the size is secondary. The compiler is free to choose whatever alignment for members is convenient subject only to the constraint that it cannot be different for different declarations of a struct. It can add space to align members or it can remove space between members. Different compilers for the same silicon can make different choices.

    For access, the compiler can perform multiple accesses to read or write unaligned members, even reading and writing neighbouring members. I think it can do this for aligned members as well, think of two byte regesters that are close to one another. This has, in fact been a practical issue with some compilers on the ARM architecture where people have used the struct construct for I/O definition. The Cortex core may not allow this distinction but IIRC ARM7 cores could be compiled with allowing unaligned access, failing silently on unaligned access or faulting on unaligned access so if you forced a struct to be unaligned the result of using unaligned members could vary widely. Compilers have been known to change access methods to a struct based on optimization, sometimes accessing a byte wide structure by using the word and masking accordingly. This can mess up both read-only status registers (think interrupt acknowledge) and write only registers that share addresses with read only registers.

    Compilers have added proprietary keywords and compile flags so that alignment can vary not only depending on the source but also the build system.

    Basically if you start using structs for hw mapping you have to consult not only the source and micro documentation but also the build flags (especially if they vary by module) and compiler documentation and even then you may not catch all the dependencies. It becomes rather difficult when you come back to a project years later and is bad enough at the time. Even then you may not catch everything (how many compilers document how access changes with optimization?) and it may change when you update the compiler.

    Unions really only add the caveat that there is no guarantee on the placement of the members or that you won't fault if you read one member after writing a different member. The first is rarely an issue when the members happen to share a size.

    Robert

    Bitfields start with the struct problems and add their own.

    Chester Gillon said:
    bitfield structures which are MISRA compliant,

    I had thought MISRA had explicitly banned bitfields for I/O access. I'm virtually certain they did on earlier versions, I've not read the latest.

  • Yes I did removed the original code, since I thought it's irrelevant to the problem I am trying to explain now. But for the timer it self I am using the full width 32 bit , so using huge numbers is not a problem. My main problem is with the algorithm of the code it self not with the style of coding, which I agree with all of you it's not readable. I am trying to find an explanation, for why when I use while((TIMER0->RIS & 0x00000001) != 1){ } and while((TIMER1->RIS & 0x00000001) != 1){ }, one of the timers stop working. While when using ( if condition ) both of the timers work normally.
  • Unreadable is a problem

    It makes it much more difficult for your readers to help you, the absence of comments does not help either. It also makes it more difficult for you to develop it correctly.

    I'd suggest it's even odds that rewriting using TivaWare would clear up the problem immediately. An additional 25 percentage points that the issue would become obvious.

    Robert
  • I wish I can change it to Tivaware, but I don't know how to. I totally changed the problem here, I added comments and graphs so I think it's easier now. If it is still not clear please let me know, coz I don't want to bother anybody. Thanks in advance