Processing Interrupts with a Basic Atom Pro

You don’t have to use assembly to use interrupts. It’s just potentially a lot faster is all.

Quick question. The sample interrupt code provided here increments a variable every 256 clock cycles. I understand that the h8 that that basic atom is based on runs at 16mhz. What is the actual speed of the atom pro I seem to recall reading that the extra code loaded on the atom causes it to run 4 times as slow. I am trying to convert the timer A value to milliseconds. So this would mean that the timer increments every .000064 seconds. Is this correct?

Also how would i change the prescaler value so the interrupt ran something like 4 times as slow?

The part about the speed of the Pro is probably better for AcidTech to answer. But as for the timer interrupt. It is based directly on the H8s processor clock. You need 16 clock cycles for a microsecond, and we are scalled to only increment TCA every 256 clock cycles. In the sample code, we have an interrupt set for every time TCA overflows. In the interrupt handler we increment a variable timer. Since TCA is only 8 bits in length it overflows once every 256 increments of TCA. So our variable timer is incremented every 256*256 clock cycles.

To figure this out, you need to look the the H8 hardware manual. In there you would find setting to 5 would set the prescaller to 512 or setting it to 6 would set it to 2048. So you have a choice of either 2 times as slow or 8 times as slow.

Good Luck

I am not sure how many other people are making use of the information about interrupts on the Atom Pro. But just in case someone else may find this information of use… I wonder if this information is best here or in the Wiki?

As part of my Brat project where I was trying to improve my communications with the PC using the DB9 serial Port(S_IN, S_OUT) I found:

IRQ0
The IO pin on the H8 that is associated with IRQ0 is P14 and on the Atom Pro, this is connected to both S_IN and S_OUT through a half duplex diode circuit. You can use this to your advantage if you would like your software on the Atom Pro to be able to detect when your PC program is trying to communicate with it, without having to leave your Pro code sitting in a SERIN. There are maybe better ways to implement this, but I will describe what has worked for me.

On the Atom Pro, I enable the interrupt on the trailing edge and implement a simple interrupt handler that reverts the I/O pin back to a normal IO pin and sets a state variable. This code looks something like:

[code]ONINTERRUPT IRQ0INT, HANDLE_IRQ0

fHostcommandPending var byte

; Initialize communications with PC
; lets setup to get interrupted if we get a rising edge on IRQ0. We can probably have the PC send a 0xff character
; which will show up as one spike up and then all zeros after as a prelude to our message header…
;
fHostcommandPending = 0
PMR1.bit4 = 1 ; enable pin to IRQ0 interrupt instead of normal I/O
IEGR1.bit0 = 0 ; Interrupt IRQ0 on falling edge
enable irq0
ienr1.bit0 = 1

HANDLE_IRQ0:
toggle p5 ; not needed, but sets led on ABB to let me know…
; disable our own interrupt.
PMR1.bit4 = 0 ; restore pin n to normal IO
fHostCommandPending = 1 ; let the main loop know that we have a packet pending
resume ; and return
[/code]

My Main loop in the Atom Pro simply tests for fHostCommandPending and calls off to a function to process the command. Something like:


main 
	' Check to see if the host PC has signalled us to process a host command
	if fHostCommandPending then
		gosub ProcessHostCommand
	endif
...
        goto main

; My processing of the code loos something like:
ProcessHostCommand:
	
	serin s_in, i2400, 1000, GCHTimeout, [str bPacketHeader\4]
	bcmd = bPacketHeader(0)
	wSeqnum.lowbyte = bPacketHeader(1)
	wSeqnum.highbyte = bPacketHeader(2)
	cbExtra = bPacketHeader(3)
	' do some simple validation of the input

	
	if ((bCmd & 0xf0) <> 0xf0) or (cbExtra >= 30) then GCHPPacketError
	
	' Now try to read in the rest of the packet, two cases, one with extra data and one without...
	if cbExtra <> 0 then
		serin s_in, i2400, 1000, GCHTimeoutExtra, [str bBuffer\cbExtra, bChksum]
	else
		serin s_in, i2400, 1000, GCHTimeoutExtra, [bchksum]
	endif
; do the processing of the command
; at end fall through... 

EndProcessHostCommand:

	; now we will reenable us as an interrupt and resume
	fHostCommandPending = 0
	PMR1.bit4 = 1 ; restore pin interrupt state
	ienr1.bit0 = 1
	return

On the PC side (VB), I first simply send a single character of hex 0xff. This turns out that at the S_IN pin as a simply pulse for the start bit. This works great to trigger the interrupt. I then have the VB code wait a bit of time for the Atom Pro to get to a point to be in a SERIN and then I output my packet of information and wait for the Atom Pro to send back an Acknowlegement packet. More handshaking could be added here, but my current code on the PC looks something like:

[code] Public Function FSendPacketandCheckforAck(ByRef com1 As IO.Ports.SerialPort, ByRef ab As Byte(), ByVal cbPacket As Short, ByVal iRetryCnt As Short) As Boolean
Dim iLoop As Short
Dim abT(1) As Byte
abT(0) = &HFF

    For iLoop = 0 To iRetryCnt
        Try
            com1.DiscardInBuffer()
            ' first output an &hff to try to get the brats attention...
            com1.Write(abT, 0, 1)
            System.Threading.Thread.Sleep(100) ' give the brat some time to get to the handler

            ' I will output this in two parts.  First the fixed part that the brat will try to read as one.
            com1.Write(ab, 0, 4) ' write out cmd, seql, seqh, cbExtra
            '                System.Threading.Thread.Sleep(30)
            com1.Write(ab, 4, cbPacket - 4)
            com1.BaseStream.Flush()

            If FCheckforAck(com1, ab(0), cbPacket, (ab(2) << 8) + ab(1)) Then
                Return True        ' we succeeded.
                System.Threading.Thread.Sleep(250) ' If an error happens wait a bit before we retry

            End If

        Catch ex As Exception

        End Try
        ' now lets wait for a hopefull ACK!!!
    Next

    Return False

End Function[/code]

Again I am not sure how many people will find this information useful. But while helping someone on the BasicMicro forum I learned more about using WTIMER. In particular about using it for capturing input signals such as PWM.

WTIMER - Input Capture

The WTimer has several capabilities, which includes the capability to interact with 4 IO pins (FTIOA-FTIOB) which on the Atom Pro is on IO pins P9-P12, these IO pins can be used for the output of wave forms or for the capturing of input signals. I will not fully describe all of these capabilities as they are described in Chapter 12 of the Renesas 3694 document.

One use of these capabilities is the ability to use the hardware to capture the pulse width of a PWM signal. You can configure the IO pins to capture the timing for the rising edge, falling edge or both. You can choose to poll the state or you can have the system cause an interrupt when a signal is detected. There are 4 registers GRA-GRD which when the signal is detected for the corresponding IO Pin FTIOA-FTIOB, will have the Wtimer’s counter stored in them by the hardware. There is also a capability to for the values for the first two Pins, where when configured, the previous value of GRA would be transferred to GRC for FTIOA(P9) or GRB will be transferred to GRD for FTIOB(P10).

Here is a little test program that shows how you might capture the input signal on P10.


TimerSave 		var word
DeltaTime		var	word
DeltaTimeLast	var	word


; Some quick and dirty inits...
TimerSave = 0
DeltaTimeLast = 0

PCR8 = 0		; Make sure all port 8 pins are input...
TCRW = 0                  ;clears TCNT and sets the timer to inc every clock cycle 
TMRW = 0x80 ;starts the timer counting 
TIOR0=0x70               ; Input capture on B and capture both rising and falling.

;TCRW= ??? - May need to set if you want to change the timer to anything but clock
;TMRW= 0xA0    ; Start counter, Have GRD buffer GRB
;TIERW=0x8A      ; Not sure here, this sets interrupts for overflow?  Input B and Input D - ???


ONINTERRUPT TIMERWINT_IMIEB,HandleFTIOB
 
ENABLE TIMERWINT_IMIEB  ;Enable the input capture 

main 
	if (DeltaTime > (DeltaTimeLast+10)) or (DeltaTimeLast > (DeltaTime + 10)) then
		DeltaTimeLast = DeltaTime;
		Serout S_OUT, i9600, [dec DeltaTimeLast,13]
		pause 500
	endif
	goto main 


handleFTIOB
	; Check the state of the IO pin to see if this was the rising or falling.
	; If rising save away the captured time.  If falling then calculate the
	; pulse width
	if IN10 <> 0 then
		; Rising edge
		TimerSave = GRB
	else
		DeltaTime = GRB - TimerSave
	endif
	
	resume

In the above program, I store away the values at the CPU clock speed. You may wish to prescale WTIMER to maybe Clock/8 or the like. This can be done by setting the appropriate value in TCRW. Also this sample program does not take into account that WTIMER may roll over during the input wave, so when you are calculating the DeltaTime you would need to verify that GRB is greater than your saved value. If it is not it would not be hard to calculate the value.

Again I want to reiterate that WTimer is used by HSERVO and may be used for other purposes in the future, so good luck.

Kurt

Again probably not of great interest, but I slight update to the above WTIMER program to scale it for input from on of my Laser 6 PWM channels, where the center will display near 1500.


TimerSave 		var word
DeltaTime		var	word
DeltaTimeLast	var	word


; Some quick and dirty inits...
TimerSave = 0
DeltaTimeLast = 0

PCR8 = 0			; Make sure all port 8 pins are input...
;TCRW = 0 			;clears TCNT and sets the timer to inc every clock cycle 
TCRW = 0x0			;clears TCNT and sets the timer to inc clock/8
TMRW = 0x80 		;starts the timer counting 
TIOR0=0x70     		; Input capture on B and capture both rising and falling.

;TCRW= ??? - May need to set if you want to change the timer to anything but clock
;TMRW= 0xA0    ; Start counter, Have GRD buffer GRB
;TIERW=0x8A      ; Not sure here, this sets interrupts for overflow?  Input B and Input D - ???


ONINTERRUPT TIMERWINT_IMIEB,HandleFTIOB
 
ENABLE TIMERWINT_IMIEB  ;Enable the input capture 

main 
	if (DeltaTime > (DeltaTimeLast+4)) or (DeltaTimeLast > (DeltaTime + 4)) then
		DeltaTimeLast = DeltaTime;
		Serout S_OUT, i9600, [dec DeltaTimeLast,13]
		pause 500
	endif
	goto main 

TSL var long
GRBL	var long
handleFTIOB
	; Check the state of the IO pin to see if this was the rising or falling.
	; If rising save away the captured time.  If falling then calculate the
	; pulse width
	low p12
	if IN10 <> 0 then
		; Rising edge
		TimerSave = GRB
		toggle p13
	else
		TSL = TimerSave
		GRBL = GRB
		if GRBL < TimerSave then
			GRBL = GRBL + 0x10000
		endif
		DeltaTime = (GRBL - TSL)/16
		toggle p14
	endif
	
	resume

Kurt

All good stuff, Kurte. I’d never have time to delve into this as much as you so I just wanted to let you know I appriciate the work.

Recently I saw a cleaner basic function for handling an encoder, which got rid of several branches by simply testing the second channels value versus the edge we are interrupting on and if they are the same we are rotating one direction, if they are different we are rotating the opposit direction. (So an XOR should handle it). So I thought I would try it in assembly language, with two IO pins P8 which IRQ1 and P10 (skipping P9 as it is our sound output…). I think I have it working now:

The basic init code:

SButtonCounter var sword
ONASMINTERRUPT IRQ1INT, HANDLE_IRQ1 

input p8		; just to be sure...
PMR1.bit5 = 1 ; enable pin to IRQ1 interrupt instead of normal I/O 
IEGR1.bit1 = 1 ; Interrupt IRQ1 on rising edge 
ENABLE IRQ1INT 

; Also make sure pin 10 is setup for input
input p10
SButtonCounter = 0 ; make sre we initialize it to zero

The assembly language handler:

[code]BEGINASMSUB
HANDLE_IRQ1
push.w r1 ; first save away R1 as we will mess with it.
bclr #1,@IRR1:8 ; clear the IRQ1 bit in the interrupt pending mask
andc #0x7f,ccr ; allow other interrupts to happen
; bset.b #5,@PCR8:8 ; make sure set to output, basic should have done earlier!
; bnot.b #5, @PDR8:8 ; Ok lets try setting one LED ON to debug

; make sure P10 is in input mode. - Should have been done earlier in basic…
; mov.b @(PDR8-0xd0+0x70), r0l ; get the current value for PCRx byte from the shadow location
; bclr.b #2, r0l ; make sure P82 is cleared
; mov.b r0l, @(PDR8-0xd0+0x70) ; update the shadow location
; mov.b r0l, @PCR8 ; update the actual direction

mov.w	@SBUTTONCOUNTER:16,r1	; Get our current count 

; get the state of Pin 10->P82, will get directly should probably setup more general...
bld.b	#2, @PDR8:8				; Get the state of Pin10 into carry
bxor.b	#1,@IEGR1:8				; XOR it with which edge we are interrupting on...
bcc		_HI1EQ:8				; Edge is equal to Pin10

dec.w	#1,r1					; edges different decrement our count
jmp		_HI1UPDC:8				; go to save our updated count

_HI1EQ:
inc.w #1,r1 ; edge is equal to increment our count

_HI1UPDC:
mov.w r1,@SBUTTONCOUNTER:16
bnot.b #1,@IEGR1:8 ; And update which edge we are looking at
pop.w r1

rte 

ENDASMSUB
[/code]

Note this code is only doing a 16 bit counter. It would be trivial to change to a 32 bit counter, by changing all of the references to the 16 bit register r1 to the 32 bit register er1 and changing the instuction from a .w to a .l
(I hope that makes sense).

FYI - The Basic code would looks something like:

... (init code would look the same except ONINTERRUPT instead)
ONASMINTERRUPT IRQ1INT, HANDLE_IRQ1 
...

HANDLE_IRQ1:
    input BSButtonPin
    BSButton = IN10
    IF (IEGR2.bit0=BSButton) THEN
        SButtonCounter=SButtonCounter+1 ;CW
    ELSE
        SButtonCounter=SButtonCounter-1 ;CCW
    ENDIF

    IEGR1.bit1 = IEGR1.bit1^1 ;0 = Pin will interrupt on a falling edge, 1 to interrupt on a rising edge.

  resume

Kurt

Actually this is really going to be useful for me so please keep posting :wink:

I am going to have my 4WD driven by a small Vaio pc sitting on the back of it.

And I intend to use the serial communication between the BotBoardII and the PC of course.

In your PC side basic code you are doing a Sleep(100), I wonder if by adding some small synchronization it would not be more robust.
A simple idea coming to mind would be to send 1 byte from the board as soon as it is ready to receive the packet.
On the PC side, having just the read of 1 byte would replace the Sleep and you could even check that the answer is correct.

Well at least I hope to use that variant (C code in my case, but similar issue).

I am tempted to include a full ring buffer implementation into the interrupt but it might take too long to process although the code is quite simple.

Anyway keep up the good work, this is really helping beginners like me :smiley:

I might post my code once I got it running to contribute a bit too.

The maintenance of the ring buffer shouldn’t be done in the interrupt, just set a flag that a character is ready, and handle the buffer in a background task.

Alan KM6VV

Yes that’s what I thought too. Always keep the code inside the interupt handler to a minimum :slight_smile:

Thanks for the confirmation :wink:

I am using Basic Micro Studio 1.0.0.15. I am trying to get the interrupt handlers to execute using a Basic Atom Pro.
I have tried using TimerA with a Pro28 and TimerB1 with a Pro40. The issue is that the interrupt bit is set indicating an interrupt occurred, but the interrupt handler does not seem to execute.
Using the code below, I can see that the timer counter (TCA for Pro28 or TCB1 for Pro40) is advancing and overflowing. The overflow bit gets set. The lTimer variable never gets set in the interrupt handler so the variable lTimer remains 0.

Thanks for any ideas.
-Bob

Basic Atom Pro28 code

lTimer var long
main
	pause 2000			; wait for connect to S_OUT port
	lTimer = 0			; clear the timer variable
	; define the interrupt handler
	ONINTERRUPT TIMERAINT, IntHandler
	; enable the interrupt
	ENABLE TIMERAINT
	IRR1.bit6 = 0		; clear the interrupt bit
mainloop
	if (lTimer <> 0) then
		serout S_OUT, I9600, "lTimer: ", dec lTimer, 13]
	endif
	serout S_OUT, I9600, "TCA: ", dec TCA, 13]
	; if interrupt, then clear the interrupt bit
	if (IRR1.bit6 = 1) then
		serout S_OUT, I9600, "timer interrupt", 13]
		IRR1.bit6 = 0
	endif
	pause 10
	goto mainloop
IntHandler
	lTimer = 1
	resume

Basic Atom Pro40 code

lTimer var long
main
	pause 2000			; wait for connect to S_OUT port
	lTimer = 0			; clear the timer variable
	; define the interrupt handler
	ONINTERRUPT TIMERB1INT, IntHandler
	; enable the interrupt
	ENABLE TIMERB1INT
	IRR2.bit5 = 0		; clear the interrupt bit
mainloop
	if (lTimer <> 0) then
		serout S_OUT, I9600, "lTimer: ", dec lTimer, 13]
	endif
	serout S_OUT, I9600, "TCB1: ", dec TCB1, 13]
	; if interrupt, then clear the interrupt bit
	if (IRR2.bit5 = 1) then
		serout S_OUT, I9600, "timer interrupt", 13]
		IRR2.bit5 = 0
	endif
	pause 10
	goto mainloop
IntHandler
	lTimer = 1
	resume

In both cases I think the problem is that you may not have enabled the processing of global interrupts. That is Enable Timeraint says that you wish to process the timerA interrupt when interrupts are enabled, but also need to tell the system thatyou wish to process interrupts. This is done by a simple: enable command. Like:

Note: I don’t think you need to clear the interrupt has occurred flag, this is done automatically by the basic interrupt handler.

Kurt

lTimer var long main pause 2000 ; wait for connect to S_OUT port lTimer = 0 ; clear the timer variable ; define the interrupt handler ONINTERRUPT TIMERAINT, IntHandler ; enable the interrupt ENABLE TIMERAINT ENABLE IRR1.bit6 = 0 ; clear the interrupt bit mainloop if (lTimer <> 0) then serout S_OUT, I9600, "lTimer: ", dec lTimer, 13] endif serout S_OUT, I9600, "TCA: ", dec TCA, 13] ; if interrupt, then clear the interrupt bit if (IRR1.bit6 = 1) then serout S_OUT, I9600, "timer interrupt", 13] IRR1.bit6 = 0 endif pause 10 goto mainloop IntHandler lTimer = 1 resume

Thank you Kurt, that did it.

I seem to be having the same issue with the C code that I am using. So I have been searching for the C equivalent of the basic ENABLE (with no arguments) to enable global interrupts.

I read this comment in a post by Nathan: “manually enable the global interrupt flag(its in the CCR register and can only be set or cleared using the special CCR specific asm commands).”
so I am trying to use assembly to set the I (Interrupt Enable) bit of the Condition Code Register (CCR)

the code below compiles in a basic file, but I can’t figure out how to compile this in a C file:

asm
{
.macro _ints_enable
bset #0,@SYSF:8
andc #0x7F,ccr
.endm
}

Any ideas?

Edit: I still don’t know how to call asm routines from C, but adding this code to start.s will enable the global interrupts in a C program by clearing the interrupt enable bit in the condition code register:
andc #0x7F,ccr

-Bob

I added predefined macros for C/C++ that let you read/modify/write the ccr register in 1.0.0.20.

Thanks Nathan,

I missed the previous post.

Thanks for the macros Nathan, that’s just what I needed.
Unfortunately, I cannot run any release beyond 1.0.0.16.
Here’s the message I get from version 1.0.0.17 through 1.0.0.23:

Starting Compiler…
This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem.

I’ve created new basic and c projects but still get the same error.

-Bob

I’m going to need more info. What OS are you using(32bit or 64bit as well). Have you installed any software that is out of the ordinary? Have you had any problems with other software recently? Do you have a second PC you can test on? Are you uninstalling all the old IDEs/Studios before install the new ones? Are you using a User account or an Admin account when running the software? What virus scanning software are you using? Are you using any other security software?

At this point no one else has reported this and I can’t reproduce it. If anyone else is having this problem please post it here.

Based on this error message and other posts I’ve found online about this error message this appears to be a VC 2008 runtime problem on your PC. Our installer installs these runtimes automatically into the installation folder but there could be something wrong with your PC causing the locally installed copy to not be detected/used. Try installing them using this link which installes them for the entire PC:

microsoft.com/downloads/deta … laylang=en

One other thing. Make sure your PC has all the updates.service packs for tour OS.

FYI, We just installed 1.0.0.23 on a 32 bit Vista machine and it went well. We can compile.

Problem is resolved. I have Visual Studio 2005 installed on my systems. The Basic Micro Studio installer did not install the VS2008 run-time, perhaps because of the VS2005 install. I manually installed VS2008 run-time and Basic Micro Studio 1.0.0.23 is now compiling my applications.
Thank you for your help!
I will be testing my Basic and C drivers with 1.0.0.23 very soon.
-Bob