CDC and timer interrupt

Coding and general discussion relating to the compiler

Moderators: David Barker, Jerry Messina

RKP
Registered User
Registered User
Posts: 82
Joined: Mon Oct 22, 2007 3:14 pm
Location: Maryland

CDC and timer interrupt

Post by RKP » Wed Apr 13, 2011 12:49 am

Has anyone gotten a timer interrupt to work with USB CDC module.

The reason I ask is the CDC works fine by itself and a timer interrupt also works fine by itself, but when they are combined neither work.

The program is pretty basic. All it does is echo back what you send it until, a certain string is received then it will turn on the timer interrupt.
This is when the program breaks

So does anyone have any suggestion as to do to make them work together.

Thanks,
RKP

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Wed Apr 13, 2011 12:45 pm

A couple of questions...

- which device?

- is USB using interrupts (ie. is USB_SERVICE = true)?
If so, make sure you set the timer to use the other priority. USB_SERVICE_PRIORITY defaults to ipHigh, so the timer would normally be ipLow.

- what's the timer ISR look like?

RKP
Registered User
Registered User
Posts: 82
Joined: Mon Oct 22, 2007 3:14 pm
Location: Maryland

Post by RKP » Wed Apr 13, 2011 2:43 pm

Jerry, I am using the 14K50 but it stops working on the 18F2455 as well.

Yes, the CDC module defaults to the USB_SERVICE= true.

Here is my set up procedure

Code: Select all

// Sub Procedures

Sub ActivateTimers()
    TMR0 = TimerValue   ' Setup Timer 0
    T0CON = %10000001   ' Enable TMR0 to 16 bits, int. Clock
                        ' Prescalar 4
    Timer0IF = 0
    Timer0IE = 1
    Timer0On = 1   
         
    Enable(OnTimers)     ' Enables Interrupt   
End Sub

Sub De_activateTimers()
    Disable(OnTimers)    ' Disables Interrupt         
End Sub     
and here is my interrupt procedure

Code: Select all

// *****************INTERRUPT************************************
// 
// 

Interrupt OnTimers(1)               ' Interrupt is low  USB Int is high by default
   High(PORTC.3)
  ' DisableISR()
   If Timer0IF = 1 Then             ' Service Timer 0 
      TMR0 = TimerValue             ' Re-load Timer 0
      Toggle(PORTC.0)       
      Timer0IF = 0                  ' Clear the Timer0 interrupt bit             
   EndIf
 '  EnableISR()   
End Interrupt
It just generates a square wave. Both work independantly but not together. I even tried to disable and enable the ISR like the library says but no luck.

In my program the CDC will function fine until I call for the activation of the timer (ActivateTimers). They just won't play well together. :?

RKP

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Wed Apr 13, 2011 2:54 pm

Interrupts on the pic default to high priority. In addition to declaring the interrupt routine as low priority, you need to change a register setting as well.

TMR0 priority is controlled with INTCON2.TMR0IP (bit 2 on the 14K50). Clear that bit to assign it to the low priority vector.

RKP
Registered User
Registered User
Posts: 82
Joined: Mon Oct 22, 2007 3:14 pm
Location: Maryland

Post by RKP » Wed Apr 13, 2011 4:06 pm

Well almost there. I added this

Code: Select all

INTCON2 = %00000000     '
IPR1 = %00000000        ' all others low priority 
IPR2 = %00000100        ' USB is high priority 
RCON.7 = 1              ' Enables priority levels of interrupts
Now the USB CDC functions even when the timer is activated.

But the timer interuupt only seem to be call when data is sent over the USB port. It toggles the led each time the send key is pressed.

The timer interrupt does not seem to be called. By the way commenting out RCON does not seem to matter.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Wed Apr 13, 2011 5:18 pm

You don't need to mess with those registers like that. You're effecting a lot of other settings, which you may not want to do if you use a different chip.

Enable() and Disable() will take care of the global interrupt enables for you as well as the IPEN bit in RCON, so all you need to do is clear INTCON2.2

Something like this should work...

Code: Select all

dim Timer0IP as INTCON2.2

Sub ActivateTimers()
    TMR0 = TimerValue   ' Setup Timer 0
    T0CON = %10000001   ' Enable TMR0 to 16 bits, int. Clock
                        ' Prescalar 4
    Timer0IP = 0        ' assign low priority intr vector
    Timer0IF = 0
    Timer0IE = 1
    Timer0On = 1   
         
    Enable(OnTimers)     ' Enables Interrupt   
End Sub

Interrupt OnTimers(1)               ' Interrupt is low  USB Int is high by default
   High(PORTC.3)
   If Timer0IF = 1 Then             ' Service Timer 0
      TMR0 = TimerValue             ' Re-load Timer 0
      Toggle(PORTC.0)       
      Timer0IF = 0                  ' Clear the Timer0 interrupt bit             
   EndIf
End Interrupt
Because of the interrupt structure on 18F's, this will only work when the high-priority interrupt is enabled (INTCON.7). Disabling high-priority interrupts also disables the low-priority ones as well. You don't have to do anything here... the high-priority intr will get enabled by the Initialize() sub in USBHID.bas/USBCDC.bas, which is called automatically for you.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Wed Apr 13, 2011 5:37 pm

Also, I just noticed you're using the timer in 16-bit mode.

If you have something like the following

Code: Select all

dim TMR0 as TMR0L.AsWord

TMR0 = TimerValue             ' Re-load Timer 0 
That won't load the 16-bit timer correctly. The two 8-bit TMR0L and TMR0H registers must be written in the order TMR0H and then TMR0L.

I use a set of macros...

Code: Select all

// write 16-bit timer specified by 'tmr' with the value 'wval'
public macro write_tmr16(tmr, wval)
    if (tmr = 0) then
        TMR0H = (wval >> 8)
        TMR0L = byte(wval)
    elseif (tmr = 1) then
        TMR1H = (wval >> 8)
        TMR1L = byte(wval)
    elseif (tmr = 3) then
        TMR3H = (wval >> 8)
        TMR3L = byte(wval)
    endif
end macro

// reads 16-bit timer specified by 'tmr', stores it in 'wresult'
public macro read_tmr16(tmr, wresult)
    if (tmr = 0) then
        wresult.byte0 = TMR0L
        wresult.byte1 = TMR0H
    elseif (tmr = 1) then
        wresult.byte0 = TMR1L
        wresult.byte1 = TMR1H
    elseif (tmr = 3) then
        wresult.byte0 = TMR3L
        wresult.byte1 = TMR3H
    endif
end macro


// example
// write_tmr16(0, TimerValue)

RKP
Registered User
Registered User
Posts: 82
Joined: Mon Oct 22, 2007 3:14 pm
Location: Maryland

Post by RKP » Wed Apr 13, 2011 9:18 pm

Thanks Jerry,

Both interrupts are now working as they should. Part of my problem was, I was using the CDC3 demo as a starting point and timer INT did not work in with it.

I switched to the CDC1 and it works just fine, thanks again.

My Timer0 seems to load the 16 bit word without any problems, not sure why but I am not going to change it.

Now for another issue. This may be for David if you looking at this or I may need to start another thread topic.
The compiler apparently limits the 14K50 to 256 bytes of RAM even though data sheet says it has more. Is there a way to use more of the available RAM.
I know there is only 256 bytes of DP RAM but is this the determining factor or something wrong in the include or .bas files?

Keith

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Thu Apr 14, 2011 9:27 am

My Timer0 seems to load the 16 bit word without any problems, not sure why but I am not going to change it
Actually it's not, but depending on what value you're loading into the timer it may look like it's working and you may not notice.

In 16-bit mode, the value written into TMRH goes into a holding register until TMRL is written, at which point the holding register is then transferred into the TMRH register. That way, the timer is updated all 16-bits at once. So, you have to do the write as TMRH then TMRL. SF doesn't guarantee the order of byte access for a multi-byte thing like a word, and in this case if you look at the assembly language result of a 16-bit write it ends up being write low-byte then write high-byte. Most times you don't care, but it won't work in this case. The macros I showed split the write into two so it can do it in the proper order. The same goes for reading a 16-bit timer, by the way.
Now for another issue. This may be for David if you looking at this or I may need to start another thread topic. The compiler apparently limits the 14K50 to 256 bytes of RAM even though data sheet says it has more. Is there a way to use more of the available RAM.
The 256 byte limit has to do with the changes done to USBsystem.bas and the USB_EXTENDED_RAM_K50 flag. I think in your code you have

Code: Select all

#if USB_EXTENDED_RAM_K50                    // Added for PIC18F13K50/14K50 family functionality                                       
      #variable _maxram = $0100             // Only 256 bytes of DP Ram <<*** THIS SETS AVAILABLE RAM, NOT DP RAM    
      #undefine EP_SIZE
      #undefine USB_RAM_EP         
      #define EP_SIZE = 4                   // endpoint size (byte)
      #define USB_RAM_EP = $200             // USB RAM start location **** starts at 200 hex on these parts         
#else
   #variable _maxram = $0200
#endif           
David had a post where he suggested

Code: Select all

#if USB_EXTENDED_RAM_K50                    // Added for PIC18F13K50/14K50 family functionality                                       
   // max user RAM
   #if _device in (18F14K50)
   #variable _maxram = $0200             // 512 bytes
   #else
   #variable _maxram = $0100             // 256 bytes
   #endif
   
   #undefine EP_SIZE
   #undefine USB_RAM_EP         
   #define EP_SIZE = 4                   // endpoint size (byte)
   #define USB_RAM_EP = $200             // USB RAM start location **** starts at 200 hex on these parts         
#else
   #variable _maxram = $0200
#endif
That'll get you back the full 512 bytes of ram.

HOWEVER, that'll conflict with one of the changes you've made in USBCDC

Code: Select all

#if USB_EXTENDED_RAM_K50                        // Added for PIC18F13K50/14K50 family functionality  
public const                                    // **********
   BufferRAM = $100,                            // **********<<< THIS WILL OVERLAY THE RAM @ $100 
   TXBufferRAM = BufferRAM,                     // **********
   RXBufferRAM = BufferRAM + 32                 // **********
public dim                                      // **********
   Buffer(104) as byte absolute BufferRAM,      // **********<<< THIS WILL OVERLAY THE RAM @ $100 
   TXBuffer(32) as byte absolute TXBufferRAM,   // **********
   RXBuffer(72) as byte absolute RXBufferRAM    // **********
#endif                                          // **********
So that won't work very well. You don't need extended ram for the K50 CDC to work, so my suggestion would be to get rid of those statements... it's not being used right now anyway, but if it was it'd cause a world of problems.


As I've said, this whole USB_EXTENDED_RAM stuff has gotten very confusing with all the disjointed modifications to the stack floating around. I'm almost done with the consolidated version I've been working on that adds support for all the 18F USB pics. Hopefully it'll help with some of these issues, although you still have to do a bit of manual management.

The main HID and CDC code is done and working, including interrupt and polled mode both with and without extended ram. I've run it on an 18F2553, 18F2450, 18F46J50, and the 14K50, which covers most of the different memory layouts (except for the 47J53... I'll check that one later). All that remains is the USB HID bootloader, which I've ported over to SF from the original C code. The bootloader is rather large (4K, same as the Microchip version), and I was hoping I could cut it down some but that doesn't look like it's going to happen since the HID code itself uses ~3K. Checking that out right now, and hope to have something in the next few days.

User avatar
ohararp
Posts: 194
Joined: Tue Oct 03, 2006 11:29 pm
Location: Dayton, OH USA
Contact:

Post by ohararp » Thu Apr 14, 2011 12:00 pm

Jerry, I just wanted to say I am interested in your efforts. You are really doing some great work here.
Thanks Ryan
$25 SMT Stencils!!!
www.ohararp.com/Stencils.html

gramo
Registered User
Registered User
Posts: 200
Joined: Tue Mar 20, 2007 6:55 am
Location: Australia
Contact:

Post by gramo » Thu Apr 14, 2011 12:59 pm

Jerry Messina wrote:I'm almost done with the consolidated version I've been working on that adds support for all the 18F USB pics. Hopefully it'll help with some of these issues, although you still have to do a bit of manual management.
As with Roshan, I too am interested in your efforts. Thank you for looking into it Jerry
digital-diy.com - Hobby microcontroller projects and tutorials. Assembly, PICBasic and C examples.

Australian distributor for the Swordfish Compiler

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Thu Apr 14, 2011 1:09 pm

Thanks.

I should probably explain a little more about using the timers in 16-bit mode, and why it would appear to work if you're using something like

Code: Select all

dim TMR0 as TMR0L.AsWord

TMR0 = TimerValue             ' Re-load Timer 0 
That code will write TMR0L then TMR0H, at least in the current version of SF.

To properly load a 16-bit timer, the sequence must be write TMRH (which goes into the holding register), then write TMRL (which transfers the holding register into TMRH). If you use the other sequence, when it writes TMRL:TMRH you will be transferring whatever's currently in the holding register into TMRH, which isn't what you want. So, the first time you write the timer it will be loaded incorrectly.

If you are reloading the timer with a constant value, the second time you write the TMRL:TMRH pair, it'll use the current contents of the holding register (which was set during the first write). Since the timer reload value isn't changing, the timer appears to have been loaded correctly while it really wasn't. The TMRH value from the second write is still in the holding register, and hasn't been transferred to TMRH.

If you reload the timer with the contents of a changing word variable (or a different value), then you'll see that this doesn't work. It's a very subtle 'bug' that's sure to have you pulling your hair out.

The same thing can happen during a 16-bit read... you must read TMRL (which latches TMRH into the holding register) and then read TMRH (you actually get the holding register contents, not TMRH).

Note: the order of writing/reading multi-byte values used to be different a few versions ago, so old code may have "gotten it right" purely by luck. If you recompile it using the newer versions, the timers may stop working.

User avatar
RangerBob
Posts: 152
Joined: Thu May 31, 2007 8:52 am
Location: Beds, UK

Post by RangerBob » Thu Apr 14, 2011 1:40 pm

Just wanted to pipe up and say I too am very interested in the work on a consolidated SF USB stack and HID bootloader you mentioned, along with Ryan and Gramo.

If it can replace my hacked 18F87J50 code, all the better! ;)

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Thu Apr 14, 2011 2:02 pm

RangerBob,

I wouldn't call what you've done hacked at all! It's very well done, and easy to follow.

All I've done (other than a few bug fixes) is to try and consolidate all the work everyone else did, and add support for all the missing chips at the same time.

I've added a new module or two to hold all the device-specific stuff in one place to make it easier to change, but it's not radically different than what was there before. Some things have changed a bit (such as ExtendedRam), but most of the other changes were mainly just to make it easier for me to follow.

One of the other goals was to be able to write the main application one way, and be able to switch between devices/interrupt/polled/extended ram use with no real source code changes, and that seems to work ok, at least so far.

User avatar
ohararp
Posts: 194
Joined: Tue Oct 03, 2006 11:29 pm
Location: Dayton, OH USA
Contact:

Post by ohararp » Thu Apr 14, 2011 2:06 pm

Jerry, I am sure you have seen this. But Joe over at PDS forum did a bunch of work with the bootloaders - http://www.protonbasic.co.uk/showthread ... rt-Thread)
Thanks Ryan
$25 SMT Stencils!!!
www.ohararp.com/Stencils.html

Post Reply