Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 20 of 20

Thread: Teensy 3.2 NVIC_ENABLE_IRQ not setting transmit interrupts

  1. #1
    Junior Member
    Join Date
    Nov 2018
    Posts
    10

    Teensy 3.2 NVIC_ENABLE_IRQ not setting transmit interrupts

    I'm testing a routine to transmit a string of characters using standard 2400_8N2 serial format on UART0, then immediately convert the Tx pin to receive a PWM response. As part of this I want to use the TC interrupt to indicate that the stop bits for the last character have been sent.
    Code:
    #define IRQ_PRIORITY  64  // 0 = highest priority, 255 = lowest
    
    byte message[2][6] = {  { 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0x00 }, 
                            { 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0x00 }  };
    int phase, modID, i;
    volatile boolean uartFlag;
    
    void setup()
    {
      Serial.begin(115200);
      Serial.println("Teensy_interrupt_Test_V3_1115a");Serial.println(" ++++++++++++++++++++++++ ");
      uart0Setup();
      Serial.print(UART0_C1, HEX);Serial.print("  ");Serial.print(UART0_C2, HEX);Serial.print("  ");Serial.print(UART0_C3, HEX);Serial.print("  ");Serial.print(UART0_C4, HEX);Serial.print("  ");Serial.println(UART0_C5, HEX);
      Serial.print(UART0_S1, HEX);Serial.print("  ");Serial.println(UART0_S2, HEX);Serial.println(SIM_SCGC5, HEX);Serial.println("  *****************************  ");
    
      for (int i = 3; i < 13; i++)
      {
        pinMode(i, OUTPUT);
        digitalWrite(i, LOW);
      }
      modID = 0;
      i = 0;
    }    // </setup()>
    
    void loop()
    {
      message[0][5] = cmdCheckSum(message[0]) | modID;
      message[1][5] = cmdCheckSum(message[1]) | modID;
    
      for (int i = 0; i < 6; i++)
      {
        UART0_D = message[0][i];
      }
      modID++;
      if (modID > 3) modID = 0;
      delay(40);
    }     // </loop()>
    
    void uart0Setup()
    {
      SIM_SCGC4 |= SIM_SCGC4_UART0;   //Turn on clock to UART0
      PORTB_PCR17 = 0x300;     // Alt 3 for PTB17(pin 1) to be UART0 output
    
      UART0_BDH = 0x07;       // 2400 bd @ 72 MHz
      UART0_BDL = 0x53;
      UART0_C2 = 0x00;        // Clear to allow FIFO setup
      UART0_PFIFO = 0xA0;     // Enable 8-bit FIFO 
      UART0_CFIFO = 0x80;     // Flush FIFO
      UART0_C1 = 0x10;        // M = 1 for 9 bits (2nd stop bit  = bit 9)
      UART0_C2 |= (UART_C2_TIE | UART_C2_TCIE | UART_C2_TE);      // Enable tx interrupts and tx function
      Serial.print(" -- ");Serial.println(UART0_C2, HEX);
      UART0_C3 = 0x40;        // T8 set for 2nd stop bit
      NVIC_SET_PRIORITY(IRQ_UART0_STATUS, IRQ_PRIORITY);
      NVIC_ENABLE_IRQ(IRQ_UART0_STATUS);
      Serial.print(" -- ");Serial.println(UART0_C2, HEX);
    }     // </uart0Setup()>
    
    void uart0_ISR()
    {
      if ((UART0_C2 & UART_C2_TCIE) && (UART0_S1 & UART_S1_TC))
      {
        uartFlag = true;
      }
    }     // </uart0_ISR()>
    
    byte cmdCheckSum(byte data[])
    {
      //  int dataLength = sizeof(data);
      int CS = 0;
      for (int i = 1; i < 5; i++)
      {
        CS = CS + data[i];
      }
      CS = CS + (CS >> 8);
      CS = CS + (CS << 4);
      CS = CS & 0xF0;
      return CS;
    }
    Even though I am explicitly setting the TCIE and TIE bits in UART0_C2, the NVIC_ENABLE_IRQ(IRQ_UART0_STATUS) function is clearing these and setting the RIE, RLIE, and RE bits, changing the register from 0xC8 to 0x3C..
    Code:
    Teensy_interrupt_Test_V3_1115a
     ++++++++++++++++++++++++ 
     -- C8
     -- 3C
    10  3C  40  0  0
    C0  0
    43F82
      *****************************
    My understanding is that NVIC_ENABLE_IRQ should simply activate the associated vector without affecting any of the associated register bits - this is obviously incorrect. If someone can enlighten me as to what is happening, I'd be grateful.

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    It's a macro:

    Code:
    #define NVIC_ENABLE_IRQ(n)    (*((volatile uint32_t *)0xE000E100 + ((n) >> 5)) = (1 << ((n) & 31)))
    You see, it updates adresses 0xE000E100 + (n) >> 5) only (and - correct - acivates the vectors only)
    It does not know anything about UARTs and does not touch them.

  3. #3
    Junior Member
    Join Date
    Nov 2018
    Posts
    10
    That's what I thought. So why does the value of UART0_C2 not remain as set? Since my original post, I've tried setting it after the NVIC_ENABLE_IRQ line and still get the same result - 0x3C as opposed to 0xC8.

  4. #4
    Junior Member
    Join Date
    Nov 2018
    Posts
    10
    I've done a lot more playing with this and think I've sorted the original problem. I now need advice on the correct procedure to use tailor-made interrupt functions with the Teensy. I think what was happening with the original issue was that I had the UART0_S1_TC bit set when I activated the interrupts, so the routine was executing the interrupt immediately. I've modified the program to avoid this.

    From further reading, I understand that the correct procedure is to simply assign the interrupt handler the name given in mk20dx128.c for the Teensy 3.2 - in my case, uart0_status_isr(void). I have done this:
    Code:
    #define IRQ_PRIORITY 64
    
    boolean uartFlag;
    void setup() 
    {
      Serial.begin(115200);
      Serial.println(" Teensy3.2_uart_int_tests_1811a");Serial.println(" ++++++++++++++++++++++++ ");
    
      uart0Setup();
      attachInterruptVector(IRQ_UART0_STATUS, uart0_status_isr);
      Serial.print(" S1 = ");Serial.println(UART0_S1, HEX);
      uint8_t a = UART0_C2;
      UART0_C2 = (UART_C2_TIE | UART_C2_TCIE | UART_C2_TE);      // Enable tx interrupts and tx function
      uint8_t b = UART0_C2;
      UART0_C2 = 0xC8;      // Enable tx interrupts and tx function
      uint8_t c = UART0_C2;
      uint8_t d = UART0_C2;
      Serial.print(" a = ");Serial.println(a, HEX);
      Serial.print(" b = ");Serial.println(b, HEX);
      Serial.print(" c = ");Serial.println(c, HEX);
      Serial.print(" d = ");Serial.println(d, HEX);
      Serial.print(" e = ");Serial.println(UART0_C2, HEX);
      Serial.print(" flag = ");Serial.println(uartFlag);
    }    // </setup()>
    
    void loop()
    {
    }
    
    void uart0Setup()
    {
      SIM_SCGC4 |= SIM_SCGC4_UART0;   //Turn on clock to UART0
      NVIC_SET_PRIORITY(IRQ_UART0_STATUS, IRQ_PRIORITY);
      NVIC_ENABLE_IRQ(IRQ_UART0_STATUS);
      PORTB_PCR17 = 0x300;     // Alt 3 for PTB17(pin 1) to be UART0 output
    
      UART0_BDH = 0x07;       // 2400 bd @ 72 MHz
      UART0_BDL = 0x53;
      UART0_C2 = 0x00;        // Clear to allow FIFO setup
      UART0_PFIFO = 0xA0;     // Enable 8-bit FIFO 
      UART0_CFIFO = 0x80;     // Flush FIFO
      UART0_C1 = 0x10;        // M = 1 for 9 bits (2nd stop bit  = bit 9)
      UART0_C2 = (UART_C2_TE);      // Enable tx function
      UART0_C3 = 0x40;        // T8 set for 2nd stop bit
    //  Serial.print(" ++ ");Serial.println(UART0_C2, HEX);
    //  UART0_C2 = 0xC8;      // Enable tx interrupts and tx function
    //  Serial.print(" ** ");Serial.println(UART0_C2, HEX);
      
    }     // </uart0Setup()>
    
    void uart0_status_isr(void)
    {
    //  if ((UART0_C2 & UART_C2_TCIE) && (UART0_S1 & UART_S1_TC))
    //  {
        uartFlag = true;
    //  }
    }     // </uart0_ISR()>
    but when compiling I get:
    Code:
    Build options changed, rebuilding all
    C:\Users\bmdur\AppData\Local\Temp\arduino_build_290251/..\arduino_cache_230823\core\core_teensy_avr_teensy31_usb_serial,speed_72,opt_o2std,keys_en-us_4939f5412a75a8560acb0f9e98268f02.a(serial1.c.o): In function `uart0_status_isr':
    
    C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/serial1.c:508: multiple definition of `uart0_status_isr'
    
    C:\Users\bmdur\AppData\Local\Temp\arduino_build_290251\sketch\Teensy3.2_uart_int_tests_1811a.ino.cpp.o:D:\Software development\Arduino\Sketches\Teensy3.2_uart_int_tests_1811a/Teensy3.2_uart_int_tests_1811a.ino:55: first defined here
    
    c:/program files (x86)/arduino/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions
    
    collect2.exe: error: ld returned 1 exit status
    
    Error compiling for board Teensy 3.2 / 3.1.
    This result is the same both with and without the attachInterruptVector() line in the code.

    Again, any advice on the correct procedure to call an interrupt handler of my choice would be gratefully received.

  5. #5
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    Simple, rename your interrupt handler.

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    7,072
    That is a 'weak' function so it can be redefined. But it is referenced in a 'c' file and your replacement is in a .INO (cpp) file?

    It may need to be wrapped something like this to avoid name mangling conflict if I'm glancing at this right:
    Code:
    #ifdef __cplusplus
    extern "C" {
    #endif
    void uart0_status_isr(void)
    {
        uartFlag = true;
    }     // </uart0_ISR()>
    #ifdef __cplusplus
    } // extern "C"
    #endif
    <edit>opps: weak in interrupt table - but defined already in Serial1.c - like the console error says
    Last edited by defragster; 11-18-2018 at 06:32 AM.

  7. #7
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    _VectorsRam[irq + 16] = function;
    irq is your irq-number, function the new name of your handler.

    On Teensy 3.x all vectors are in RAM an can be changed at runtime.

    https://github.com/PaulStoffregen/co..._teensy.c#L191

  8. #8
    Junior Member
    Join Date
    Nov 2018
    Posts
    10
    @Frank _B -
    If I change my interrupt routine, it brings me back to my original problem - and I don't hit the interrupt handler.
    Code:
    #define IRQ_PRIORITY 64
    
    boolean uartFlag;
    void setup() 
    {
      Serial.begin(115200);
      Serial.println(" Teensy3.2_uart_int_tests_1811a");Serial.println(" ++++++++++++++++++++++++ ");
    
      uart0Setup();
    //  attachInterruptVector(IRQ_UART0_STATUS, uart0_status_hdlr);
      Serial.print(" S1 = 0x");Serial.println(UART0_S1, HEX);
      uint8_t a = UART0_C2;
      UART0_C2 = (UART_C2_TIE | UART_C2_TCIE | UART_C2_TE);      // Enable tx interrupts and tx function
      uint8_t b = UART0_C2;
      UART0_C2 = 0xC8;      // Enable tx interrupts and tx function
      uint8_t c = UART0_C2;
      uint8_t d = UART0_C2;
      Serial.print(" a = 0x");Serial.println(a, HEX);
      Serial.print(" b = 0x");Serial.println(b, HEX);
      Serial.print(" c = 0x");Serial.println(c, HEX);
      Serial.print(" d = 0x");Serial.println(d, HEX);
      Serial.print(" e = 0x");Serial.println(UART0_C2, HEX);
      Serial.print(" flag = ");Serial.println(uartFlag);
    }    // </setup()>
    
    void loop()
    {
    }
    
    void uart0Setup()
    {
      SIM_SCGC4 |= SIM_SCGC4_UART0;   //Turn on clock to UART0
      NVIC_SET_PRIORITY(IRQ_UART0_STATUS, IRQ_PRIORITY);
      NVIC_ENABLE_IRQ(IRQ_UART0_STATUS);
      PORTB_PCR17 = 0x300;     // Alt 3 for PTB17(pin 1) to be UART0 output
    
      UART0_BDH = 0x07;       // 2400 bd @ 72 MHz
      UART0_BDL = 0x53;
      UART0_C2 = 0x00;        // Clear to allow FIFO setup
      UART0_PFIFO = 0xA0;     // Enable 8-bit FIFO 
      UART0_CFIFO = 0x80;     // Flush FIFO
      UART0_C1 = 0x10;        // M = 1 for 9 bits (2nd stop bit  = bit 9)
      UART0_C2 = (UART_C2_TE);      // Enable tx function
      UART0_C3 = 0x40;        // T8 set for 2nd stop bit
    //  Serial.print(" ++ ");Serial.println(UART0_C2, HEX);
    //  UART0_C2 = 0xC8;      // Enable tx interrupts and tx function
    //  Serial.print(" ** ");Serial.println(UART0_C2, HEX);
      
    }     // </uart0Setup()>
    
    void uart0_status_hdlr(void)
    {
    //  if ((UART0_C2 & UART_C2_TCIE) && (UART0_S1 & UART_S1_TC))
    //  {
        uartFlag = true;
    //  }
    }     // </uart0_ISR()>
    gives as output:
    Code:
     Teensy3.2_uart_int_tests_1811a
     ++++++++++++++++++++++++ 
     S1 = 0xC0
     a = 0x8
     b = 0xC8
     c = 0xC8
     d = 0x3C
     e = 0x3C
     flag = 0
    So when starting the interrupt flags in S1 are both set, as expected. My uart0Setup only enables the transmit function - it does not enable any interrupts (result a). Explicitly setting the interrupts ddoes enable them, and I would expect the interrupt routine to be executed at that stage. This should set uartFlag.
    The results b, c, and d are for consecutive reads of UART0_C2. There are no writes to UART0_C2 between the assignment functions for c and d, yet the value has changed.

    It appears to me that the interrupt is being called, but not the one I've written. Any ideas as to other lines of investigation?

  9. #9
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    Maybe, but this is not a problem with the vector.
    Try this:
    Code:
    //original vector
    void (*prevInterruptPtr)(void);
    
    void uart0_status_hdlr(void) {
     digitalWriteFast(13, !digitalReadFast(13)); 
     
     //Just for fun, call original vector now :-) - This is called interrupt chaining. You don't need it, but its fun...:)
     prevInterruptPtr();
    }
    
    void setup() {
     pinMode(13, OUTPUT); 
     prevInterruptPtr = _VectorsRam[IRQ_UART0_STATUS + 16];
     Serial1.begin(9600);
    
     attachInterruptVector(IRQ_UART0_STATUS, uart0_status_hdlr); 
    }
    
    void loop() {
      Serial1.print("x");  
      delay(100);
      }
    You see, it works. With any vector.

    Edit: for your problem, i'd take a close look what the original vector does, and copy that. You know where the sourcecode is?

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,669
    Quote Originally Posted by bmd1103 View Post
    ... to transmit a string of characters using standard 2400_8N2 serial format on UART0, then immediately convert the Tx pin to receive a PWM response.
    Why go to all this trouble to craft your own ISR when the supplied code provides Serial1.flush() and Serial1.end() ?

  11. #11
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,669
    My guess is you're going to answer that you may believe the supplied Serial1 code isn't good enough for your project's tight timing requirements? If that's really your motivation, I'd like to ask whether you've actually tested it?

    I ran a quick test just now, with this very simple program.

    Code:
    void setup() {
      pinMode(4, OUTPUT);
    }
    
    void loop() {
      Serial1.begin(2400, SERIAL_8N2);
      Serial1.print("Hello World");
      Serial1.flush();
      Serial1.end();
      digitalWriteFast(4, HIGH);
      delay(1);
      digitalWriteFast(4, LOW);
      delay(100);
    }
    I connected two 10K resistors to the TX1 signal (digital pin 1), so when the TX1 transmitter shuts off the signal will "float" to a distinct voltage that isn't logic low or logic high. This code also creates a pulse after Serial1.end() finishes, so we can see how quickly your PWM measuring code might be able to run. Here's how the waveforms look my oscilloscope.

    Click image for larger version. 

Name:	file.png 
Views:	5 
Size:	29.2 KB 
ID:	15181

    As another quick test, I changed SERIAL_8N2 to SERIAL_8N1, so it only transmits 1 stop bit. Here's the result.

    Click image for larger version. 

Name:	file.png 
Views:	7 
Size:	29.2 KB 
ID:	15182

    It's pretty clear to see the speed of turning off the serial port and running other code is so much faster than 1 stop bit time, at least at this fairly slow baud rate. So much trouble to mess with custom interrupts hardly seems worthwhile.


    Then again, if your answer is you simply love playing with interrupts and hardware registers, of course keep going. But if you just want to get your project working, maybe try using the existing Serial1 functions. They work very well.

  12. #12
    Junior Member
    Join Date
    Nov 2018
    Posts
    10
    Paul - before I reply I'd like to express my appreciation for the time and effort you put in to the Teensy system.

    As someone who has been working with computers for over 50 years, one of my motives for working on things like this is to come to grips with the "hows" and "why"s of what is done - as well as making good use of the results in one way or another. Hence my desire to understand just how 2 consecutive reads of a single register can produce different results.

    My current project could involve up to 4 or 5 serial channels, all using the same protocol, and I am attempting to forestall possible problems, both with the control of that many uarts all at once and with the pin interrupts and FTMs that I'll be introducing later. I have successfully used the standard Serial1 routines. I'll try chaining the interrupts to see what happens.

    Thanks for your time and support.

  13. #13
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    Err.. no!! The chain was an example only. This way i did'nt have to bother with the internals of that interrupt, it was a shortcut - I wrote that code in 3 minutes.
    It should show, that your vector gets called correctly if you use attachInterruptVector() (edit: you suspected problems).
    Quote Originally Posted by bmd1103
    It appears to me that the interrupt is being called, but not the one I've written. Any ideas as to other lines of investigation?
    The example blinks the LED.
    Best is, to look at the the existing code and learn how it handles the registers.

    But Paul is right. Keep it simple!

    Sorry if my my post was not clear - My English is not good.
    Last edited by Frank B; 11-18-2018 at 07:19 PM.

  14. #14
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765

  15. #15
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    7,072
    @Frank_B - it worked and made sense to me. The new _isr() was indeed called and the LED flashed - and with the chain call to the old _isr() with "prevInterruptPtr();" it worked.

  16. #16
    Junior Member
    Join Date
    Nov 2018
    Posts
    10
    I have looked at the existing code and seen how it handles the registers. Because I'm dealing with messages that are only 6 bytes long, and am not using the read side of the serial port, I have no need for the circular buffers (at least with Serial1). I want to dump 6 bytes into the FIFO, do a lot of other stuff while the message is being processed, and come back to the communications when the TC flag is set. For this to work without polling, I'd have to initiate the read code within the serial interrupt handler. I'd like to try initiating the pin interrupts within the UART handler, then turn the serial transmitter off until the receive transaction is complete.

  17. #17
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    Thank you Defragster.

    Well, I would it do this way (If I really wanted to play with it):

    1) copy the code
    2) remove the unneeded stuff
    - now you have the essential code that deals with the registers, and you can learn how they are used -
    3) add your code

  18. #18
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,669
    Agreed, just copying the existing code would save a lot of time. This could probably be done the desired way by just adding 1 callback from the ISR for the transmit complete. I suppose the overhead of first copying the data to the buffer before putting it all into the FIFO could also be eliminated. Whether that's worthwhile, for only 6 bytes that take 25 ms to transmit is questionable.

  19. #19
    Junior Member
    Join Date
    Nov 2018
    Posts
    10
    I guess one of the things I need to come to grips with is that with the Teensy 3.2 etc there isn't quite the need to conserve memory and cycle time as there is with the chips I've been working with!

  20. #20
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,765
    Yes, it is.
    About speed: For the T3.6 there is a C64 emulator - it emulates _all_ the hardware - and handles its interfaces (e.g. Serial IEC and a UART) with bitbanging in the software......... partly in the emulated 6502 assembler (c64-rom routines).....

    The teensy are underestimated by many people.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •