How to know if an interrupted fired.

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I set an int. then loop, how can I check if the int. fired?

 

EIMSK |= (1 << INT0); //enable interrupt.

Dostuff(){};

if (x) {//int fired

}

 

 

 

Last Edited: Wed. Jul 11, 2018 - 02:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When the interrupt fires, then it is going to vector to the isr which will probably clear the interrupt flag, so unless you provide another mechanism, then your loop won’t know. If you don’t enable the interrupt, you can poll the interrupt flag in your loop.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First, your variable "x" has to be visible to both main(){} and your interrupt code.

You can do that by declaring it outside any function. It will then have "global scope".

 

Next, GCC tries real hard to use all kind of tricks to make your code run faster, which is called "optimisation".

Interrupt routines run outside the normal program flow, and the optimisation gets a bit confused here.

Therefore you have to use the "volatile" keyword to tell to the compiler it can't do certain optimisations.

 

Third: Nowaday's it is a bad habit to use single character variables.

It makes it harder to do search operations through code. For example, I use Qt Creator, and that IDE automatically highliths al uses of a var if I double click on it, but that only works with keywords with a minimum length of 3 characters.

 

Fourth: try to post complet code samples, and use the code tags: "< > " icon nect to "Ohmega" above the edit window.

Your code will look something like:

# include <avr/interrupt>

#define INT_FIRED       0x01
#define INT_SOMETHING   0x02

volatile uint8_t interruptFlags;

SIGNAL SIG_INT0() {
    interruptFlags | = INT_FIRED;
    // Extra: Turn on LED or something.

}

void main( void) {
    EIMSK |= (1 << INT0);   // enable interrupt.
    sei( );                 // Enable global interrupts.

    for( ;;) {
        Dostuff( );

        if ( interruptFlags & INT_FIRED) {
            interruptFlags &= ~INT_FIRED;
            // Extra: Turn off LED or something.
            // Add code...
        }
    }
}

 

 

 

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

Last Edited: Wed. Jul 11, 2018 - 04:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Put a debugger breakpoint in the ISR. You will know then when it fires. 

 

If you are talking about code that "knows" when an interrupt has fired, set a volatile flag in the interrupt. Test for, and clear said flag in your code. I do this, for example, with an external interrupt that responds to a 100Hz alert pulse from a sensor. The interrupt counts down by 100 (with a static software counter) and sets a SecondFlag (volatile)  when the counter rolls over.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Jul 11, 2018 - 03:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Paulvdh wrote:
GCC tries real hard to use all kind of tricks to make your code run faster, which is called "optimisation".

 

It's not just GCC - this is Standard Practise for any decent, modern compiler.

 

See: https://www.avrfreaks.net/commen...

 

As the others have said, 'volatile' is the key here ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Would have helped if you said which model of AVR. As EIMSK has been mentioned I will take a guess at 328P perhaps? If so then as well as EIMSK to say which interrupt you want there is an EIFR that latches when any kind of interrupt occurs. So your most immediate way to know that the event has occurred is to forget even using an ISR() and simply make:

EIMSK |= (1 << INT0); //enable interrupt.
Dostuff(){};
if (x) {//int fired
}

be:

EIMSK |= (1 << INT0); //enable interrupt.
Dostuff(){};
if (EIFR & (1 << INTF0)) {//int fired
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thx all, I was aware of the variable flag trick and do that in many cases. I should have been more clear in wanting to know how I could track the fired int. based on a flag register. I also did mean to mention the atmega328... It was late.

 

so EIFR, that maybe what I'm after. but not sure I get the set unset state? Do I need to clear first?

 

EIFR &=  ~(1 << INTF0)//clear bit

loop

if (EIFR & (1 << INTF0))test bit

 

The nature of this interrupt is that is fires once a ms. I don't start the interrupt but during my loop I just need to know if it occurred.

 

 

I could still use the flag trick but I'm having a hard time knowing where to put it? It's in this file I'm sure. I was thinking the label storeTokenAndReturn was the exit form the interrupt being fired.

/* Name: asmcommon.inc
 * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
 * Author: Christian Starkjohann
 * Creation Date: 2007-11-05
 * Tabsize: 4
 * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
 */

/* Do not link this file! Link usbdrvasm.S instead, which includes the
 * appropriate implementation!
 */

/*
General Description:
This file contains assembler code which is shared among the USB driver
implementations for different CPU cocks. Since the code must be inserted
in the middle of the module, it's split out into this file and #included.

Jump destinations called from outside:
    sofError: Called when no start sequence was found.
    se0: Called when a package has been successfully received.
    overflow: Called when receive buffer overflows.
    doReturn: Called after sending data.

Outside jump destinations used by this module:
    waitForJ: Called to receive an already arriving packet.
    sendAckAndReti:
    sendNakAndReti:
    sendCntAndReti:
    usbSendAndReti:

The following macros must be defined before this file is included:
    .macro POP_STANDARD
    .endm
    .macro POP_RETI
    .endm
*/

#define token   x1

overflow:
    ldi     x2, 1<<USB_INTR_PENDING_BIT
    USB_STORE_PENDING(x2)       ; clear any pending interrupts
ignorePacket:
    clr     token
    rjmp    storeTokenAndReturn

;----------------------------------------------------------------------------
; Processing of received packet (numbers in brackets are cycles after center of SE0)
;----------------------------------------------------------------------------
;This is the only non-error exit point for the software receiver loop
;we don't check any CRCs here because there is no time left.
se0:
    subi    cnt, USB_BUFSIZE    ;[5]
    neg     cnt                 ;[6]
    sub     YL, cnt             ;[7]
    sbci    YH, 0               ;[8]
    ldi     x2, 1<<USB_INTR_PENDING_BIT ;[9]
    USB_STORE_PENDING(x2)       ;[10] clear pending intr and check flag later. SE0 should be over.
    ld      token, y            ;[11]
    cpi     token, USBPID_DATA0 ;[13]
    breq    handleData          ;[14]
    cpi     token, USBPID_DATA1 ;[15]
    breq    handleData          ;[16]
    lds     shift, usbDeviceAddr;[17]
    ldd     x2, y+1             ;[19] ADDR and 1 bit endpoint number
    lsl     x2                  ;[21] shift out 1 bit endpoint number
    cpse    x2, shift           ;[22]
    rjmp    ignorePacket        ;[23]
/* only compute endpoint number in x3 if required later */
#if USB_CFG_HAVE_INTRIN_ENDPOINT || USB_CFG_IMPLEMENT_FN_WRITEOUT
    ldd     x3, y+2             ;[24] endpoint number + crc
    rol     x3                  ;[26] shift in LSB of endpoint
#endif
    cpi     token, USBPID_IN    ;[27]
    breq    handleIn            ;[28]
    cpi     token, USBPID_SETUP ;[29]
    breq    handleSetupOrOut    ;[30]
    cpi     token, USBPID_OUT   ;[31]
    brne    ignorePacket        ;[32] must be ack, nak or whatever
;   rjmp    handleSetupOrOut    ; fallthrough

;Setup and Out are followed by a data packet two bit times (16 cycles) after
;the end of SE0. The sync code allows up to 40 cycles delay from the start of
;the sync pattern until the first bit is sampled. That's a total of 56 cycles.
handleSetupOrOut:               ;[32]
#if USB_CFG_IMPLEMENT_FN_WRITEOUT   /* if we have data for endpoint != 0, set usbCurrentTok to address */
    andi    x3, 0xf             ;[32]
    breq    storeTokenAndReturn ;[33]
    mov     token, x3           ;[34] indicate that this is endpoint x OUT
#endif
storeTokenAndReturn:
    sts     usbCurrentTok, token;[35]
doReturn:
    POP_STANDARD                ;[37] 12...16 cycles
    USB_LOAD_PENDING(YL)        ;[49]
    sbrc    YL, USB_INTR_PENDING_BIT;[50] check whether data is already arriving
    rjmp    waitForJ            ;[51] save the pops and pushes -- a new interrupt is already pending
sofError:
    POP_RETI                    ;macro call
	RETI

handleData:
#if USB_CFG_CHECK_CRC
    CRC_CLEANUP_AND_CHECK       ; jumps to ignorePacket if CRC error
#endif
    lds     shift, usbCurrentTok;[18]
    tst     shift               ;[20]
    breq    doReturn            ;[21]
    lds     x2, usbRxLen        ;[22]
    tst     x2                  ;[24]
    brne    sendNakAndReti      ;[25]
; 2006-03-11: The following two lines fix a problem where the device was not
; recognized if usbPoll() was called less frequently than once every 4 ms.
    cpi     cnt, 4              ;[26] zero sized data packets are status phase only -- ignore and ack
    brmi    sendAckAndReti      ;[27] keep rx buffer clean -- we must not NAK next SETUP
#if USB_CFG_CHECK_DATA_TOGGLING
    sts     usbCurrentDataToken, token  ; store for checking by C code
#endif
    sts     usbRxLen, cnt       ;[28] store received data, swap buffers
    sts     usbRxToken, shift   ;[30]
    lds     x2, usbInputBufOffset;[32] swap buffers
    ldi     cnt, USB_BUFSIZE    ;[34]
    sub     cnt, x2             ;[35]
    sts     usbInputBufOffset, cnt;[36] buffers now swapped
    rjmp    sendAckAndReti      ;[38] 40 + 17 = 57 until SOP

handleIn:
;We don't send any data as long as the C code has not processed the current
;input data and potentially updated the output data. That's more efficient
;in terms of code size than clearing the tx buffers when a packet is received.
    lds     x1, usbRxLen        ;[30]
    cpi     x1, 1               ;[32] negative values are flow control, 0 means "buffer free"
    brge    sendNakAndReti      ;[33] unprocessed input packet?
    ldi     x1, USBPID_NAK      ;[34] prepare value for usbTxLen
#if USB_CFG_HAVE_INTRIN_ENDPOINT
    andi    x3, 0xf             ;[35] x3 contains endpoint
#if USB_CFG_SUPPRESS_INTR_CODE
    brne    sendNakAndReti      ;[36]
#else
    brne    handleIn1           ;[36]
#endif
#endif
    lds     cnt, usbTxLen       ;[37]
    sbrc    cnt, 4              ;[39] all handshake tokens have bit 4 set
    rjmp    sendCntAndReti      ;[40] 42 + 16 = 58 until SOP
    sts     usbTxLen, x1        ;[41] x1 == USBPID_NAK from above
    ldi     YL, lo8(usbTxBuf)   ;[43]
    ldi     YH, hi8(usbTxBuf)   ;[44]
    rjmp    usbSendAndReti      ;[45] 57 + 12 = 59 until SOP

; Comment about when to set usbTxLen to USBPID_NAK:
; We should set it back when we receive the ACK from the host. This would
; be simple to implement: One static variable which stores whether the last
; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the
; ACK. However, we set it back immediately when we send the package,
; assuming that no error occurs and the host sends an ACK. We save one byte
; RAM this way and avoid potential problems with endless retries. The rest of
; the driver assumes error-free transfers anyway.

#if !USB_CFG_SUPPRESS_INTR_CODE && USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */
handleIn1:                      ;[38]
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint
    cpi     x3, USB_CFG_EP3_NUMBER;[38]
    breq    handleIn3           ;[39]
#endif
    lds     cnt, usbTxLen1      ;[40]
    sbrc    cnt, 4              ;[42] all handshake tokens have bit 4 set
    rjmp    sendCntAndReti      ;[43] 47 + 16 = 63 until SOP
    sts     usbTxLen1, x1       ;[44] x1 == USBPID_NAK from above
    ldi     YL, lo8(usbTxBuf1)  ;[46]
    ldi     YH, hi8(usbTxBuf1)  ;[47]
    rjmp    usbSendAndReti      ;[48] 50 + 12 = 62 until SOP

#if USB_CFG_HAVE_INTRIN_ENDPOINT3
handleIn3:
    lds     cnt, usbTxLen3      ;[41]
    sbrc    cnt, 4              ;[43]
    rjmp    sendCntAndReti      ;[44] 49 + 16 = 65 until SOP
    sts     usbTxLen3, x1       ;[45] x1 == USBPID_NAK from above
    ldi     YL, lo8(usbTxBuf3)  ;[47]
    ldi     YH, hi8(usbTxBuf3)  ;[48]
    rjmp    usbSendAndReti      ;[49] 51 + 12 = 63 until SOP
#endif
#endif

 

Last Edited: Wed. Jul 11, 2018 - 05:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For most interrupts (but not all) the flag that make a ISR fire get's cleared when the interrupt get's take.

If you pull you will need to clear it manually, so wait until set, then clear it (normally on a AVR it's by writing a 1!)

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not having much luck with the interrupts but was wondering could I make this work somehow?

 

    sleep_enable();
    sleep_cpu();
    sleep_disable();

    //CPU should wake  on interrupt

 

but its not working. I don't think the interrupt is waking it.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Where's the ISR to handle the interrupt?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Could the isr be another name in asm? I looked for "isr" in all the v-usb code and could not find it. It is clearly there.

Last Edited: Fri. Jul 13, 2018 - 12:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
Could the isr be another name in asm? 

Yes.

 

The hardware neither knows nor cares about names used in source code - all that matters is that the ISR is (or starts) at the appropriate vector address.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found this

USB_INTR_VECTOR:

Guessing that is the ISR I need but its in a few inc files. (usbdrvasn***.inc ).

 

then I found the main usbdrvasm.S has these includes and  this

/*
General Description:
This module is the assembler part of the USB driver. This file contains
general code (preprocessor acrobatics and CRC computation) and then includes
the file appropriate for the given clock rate.
*/

 

this file also has this

 

 COMMON  INTVEC
#   ifndef USB_INTR_VECTOR
        ORG     INT0_vect
#   else /* USB_INTR_VECTOR */
        ORG     USB_INTR_VECTOR
#       undef   USB_INTR_VECTOR
#   endif /* USB_INTR_VECTOR */
#   define  USB_INTR_VECTOR usbInterruptHandler
    rjmp    USB_INTR_VECTOR
    RSEG    CODE

 

So That must be how all of that works. My inc is usbdrvasm12

 

 

Now I'm a tad bit rusty on how the ASM works as I don't stare at ASM all day. but I'm sure what I need is in this file.

 

However I do not think the usbdrvasm is the file I need, I think it's the included code. Below is the 12 mhz version that I use.

 

So two questions remain

 

1) Assuming the code below is the right file. Where exactly is it?
2) How can I access say a global extern   char _SOF_Flag  in ASM.

 

I'm thinking I want to target this section. Because the SOF I believe are the 1ms frames and I think he keep track of them. I may just need my global flag where is says USB_SOF_HOOK. From what I read the USB_SOF_HOOK can not be used in my case because of the d- d+ pin setup. Or possibly I just need the global flag set at foundK but I don't want to mess up the timing.

 

The timing is so tight I have no room, all jumps are rjumps and no NOPS exist. Maybe since I don't define that sof hook I could

replace

#ifdef USB_SOF_HOOK
    USB_SOF_HOOK
#endif

with a push  to my global flag?

 

#if USB_COUNT_SOF
    lds     YL, usbSofCount
    inc     YL
    sts     usbSofCount, YL
#endif  /* USB_COUNT_SOF */
#ifdef USB_SOF_HOOK
    USB_SOF_HOOK
#endif
    rjmp    sofError

 

 

 

 

/* Name: usbdrvasm12.inc
 * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
 * Author: Christian Starkjohann
 * Creation Date: 2004-12-29
 * Tabsize: 4
 * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
 */

/* Do not link this file! Link usbdrvasm.S instead, which includes the
 * appropriate implementation!
 */

/*
General Description:
This file is the 12 MHz version of the asssembler part of the USB driver. It
requires a 12 MHz crystal (not a ceramic resonator and not a calibrated RC
oscillator).

See usbdrv.h for a description of the entire driver.

Since almost all of this code is timing critical, don't change unless you
really know what you are doing! Many parts require not only a maximum number
of CPU cycles, but even an exact number of cycles!

Timing constraints according to spec (in bit times):
timing subject                                      min max    CPUcycles
---------------------------------------------------------------------------
EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2   16     16-128
EOP of IN to sync pattern of DATA0 (rx, then tx)    2   7.5    16-60
DATAx (rx) to ACK/NAK/STALL (tx)                    2   7.5    16-60
*/

;Software-receiver engine. Strict timing! Don't change unless you can preserve timing!
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled
;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable
;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes
;Numbers in brackets are maximum cycles since SOF.
USB_INTR_VECTOR:
;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt
    push    YL              ;2 [35] push only what is necessary to sync with edge ASAP
    in      YL, SREG        ;1 [37]
    push    YL              ;2 [39]
;----------------------------------------------------------------------------
; Synchronize with sync pattern:
;----------------------------------------------------------------------------
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K]
;sync up with J to K edge during sync pattern -- use fastest possible loops
;The first part waits at most 1 bit long since we must be in sync pattern.
;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to
;waitForJ, ensure that this prerequisite is met.
waitForJ:
    inc     YL
    sbis    USBIN, USBMINUS
    brne    waitForJ        ; just make sure we have ANY timeout
waitForK:
;The following code results in a sampling window of 1/4 bit which meets the spec.
    sbis    USBIN, USBMINUS
    rjmp    foundK
    sbis    USBIN, USBMINUS
    rjmp    foundK
    sbis    USBIN, USBMINUS
    rjmp    foundK
    sbis    USBIN, USBMINUS
    rjmp    foundK
    sbis    USBIN, USBMINUS
    rjmp    foundK
#if USB_COUNT_SOF
    lds     YL, usbSofCount
    inc     YL
    sts     usbSofCount, YL
#endif  /* USB_COUNT_SOF */
#ifdef USB_SOF_HOOK
    USB_SOF_HOOK
#endif
    rjmp    sofError
foundK:
;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling]
;we have 1 bit time for setup purposes, then sample again. Numbers in brackets
;are cycles from center of first sync (double K) bit after the instruction
    push    YH                  ;2 [2]
    lds     YL, usbInputBufOffset;2 [4]
    clr     YH                  ;1 [5]
    subi    YL, lo8(-(usbRxBuf));1 [6]
    sbci    YH, hi8(-(usbRxBuf));1 [7]

    sbis    USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early]
    rjmp    haveTwoBitsK    ;2 [10]
    pop     YH              ;2 [11] undo the push from before
    rjmp    waitForK        ;2 [13] this was not the end of sync, retry
haveTwoBitsK:
;----------------------------------------------------------------------------
; push more registers and initialize values while we sample the first bits:
;----------------------------------------------------------------------------
    push    shift           ;2 [16]
    push    x1              ;2 [12]
    push    x2              ;2 [14]

    in      x1, USBIN       ;1 [17] <-- sample bit 0
    ldi     shift, 0xff     ;1 [18]
    bst     x1, USBMINUS    ;1 [19]
    bld     shift, 0        ;1 [20]
    push    x3              ;2 [22]
    push    cnt             ;2 [24]

    in      x2, USBIN       ;1 [25] <-- sample bit 1
    ser     x3              ;1 [26] [inserted init instruction]
    eor     x1, x2          ;1 [27]
    bst     x1, USBMINUS    ;1 [28]
    bld     shift, 1        ;1 [29]
    ldi     cnt, USB_BUFSIZE;1 [30] [inserted init instruction]
    rjmp    rxbit2          ;2 [32]

;----------------------------------------------------------------------------
; Receiver loop (numbers in brackets are cycles within byte after instr)
;----------------------------------------------------------------------------

unstuff0:               ;1 (branch taken)
    andi    x3, ~0x01   ;1 [15]
    mov     x1, x2      ;1 [16] x2 contains last sampled (stuffed) bit
    in      x2, USBIN   ;1 [17] <-- sample bit 1 again
    ori     shift, 0x01 ;1 [18]
    rjmp    didUnstuff0 ;2 [20]

unstuff1:               ;1 (branch taken)
    mov     x2, x1      ;1 [21] x1 contains last sampled (stuffed) bit
    andi    x3, ~0x02   ;1 [22]
    ori     shift, 0x02 ;1 [23]
    nop                 ;1 [24]
    in      x1, USBIN   ;1 [25] <-- sample bit 2 again
    rjmp    didUnstuff1 ;2 [27]

unstuff2:               ;1 (branch taken)
    andi    x3, ~0x04   ;1 [29]
    ori     shift, 0x04 ;1 [30]
    mov     x1, x2      ;1 [31] x2 contains last sampled (stuffed) bit
    nop                 ;1 [32]
    in      x2, USBIN   ;1 [33] <-- sample bit 3
    rjmp    didUnstuff2 ;2 [35]

unstuff3:               ;1 (branch taken)
    in      x2, USBIN   ;1 [34] <-- sample stuffed bit 3 [one cycle too late]
    andi    x3, ~0x08   ;1 [35]
    ori     shift, 0x08 ;1 [36]
    rjmp    didUnstuff3 ;2 [38]

unstuff4:               ;1 (branch taken)
    andi    x3, ~0x10   ;1 [40]
    in      x1, USBIN   ;1 [41] <-- sample stuffed bit 4
    ori     shift, 0x10 ;1 [42]
    rjmp    didUnstuff4 ;2 [44]

unstuff5:               ;1 (branch taken)
    andi    x3, ~0x20   ;1 [48]
    in      x2, USBIN   ;1 [49] <-- sample stuffed bit 5
    ori     shift, 0x20 ;1 [50]
    rjmp    didUnstuff5 ;2 [52]

unstuff6:               ;1 (branch taken)
    andi    x3, ~0x40   ;1 [56]
    in      x1, USBIN   ;1 [57] <-- sample stuffed bit 6
    ori     shift, 0x40 ;1 [58]
    rjmp    didUnstuff6 ;2 [60]

; extra jobs done during bit interval:
; bit 0:    store, clear [SE0 is unreliable here due to bit dribbling in hubs]
; bit 1:    se0 check
; bit 2:    overflow check
; bit 3:    recovery from delay [bit 0 tasks took too long]
; bit 4:    none
; bit 5:    none
; bit 6:    none
; bit 7:    jump, eor
rxLoop:
    eor     x3, shift   ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others
    in      x1, USBIN   ;1 [1] <-- sample bit 0
    st      y+, x3      ;2 [3] store data
    ser     x3          ;1 [4]
    nop                 ;1 [5]
    eor     x2, x1      ;1 [6]
    bst     x2, USBMINUS;1 [7]
    bld     shift, 0    ;1 [8]
    in      x2, USBIN   ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed)
    andi    x2, USBMASK ;1 [10]
    breq    se0         ;1 [11] SE0 check for bit 1
    andi    shift, 0xf9 ;1 [12]
didUnstuff0:
    breq    unstuff0    ;1 [13]
    eor     x1, x2      ;1 [14]
    bst     x1, USBMINUS;1 [15]
    bld     shift, 1    ;1 [16]
rxbit2:
    in      x1, USBIN   ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed)
    andi    shift, 0xf3 ;1 [18]
    breq    unstuff1    ;1 [19] do remaining work for bit 1
didUnstuff1:
    subi    cnt, 1      ;1 [20]
    brcs    overflow    ;1 [21] loop control
    eor     x2, x1      ;1 [22]
    bst     x2, USBMINUS;1 [23]
    bld     shift, 2    ;1 [24]
    in      x2, USBIN   ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed)
    andi    shift, 0xe7 ;1 [26]
    breq    unstuff2    ;1 [27]
didUnstuff2:
    eor     x1, x2      ;1 [28]
    bst     x1, USBMINUS;1 [29]
    bld     shift, 3    ;1 [30]
didUnstuff3:
    andi    shift, 0xcf ;1 [31]
    breq    unstuff3    ;1 [32]
    in      x1, USBIN   ;1 [33] <-- sample bit 4
    eor     x2, x1      ;1 [34]
    bst     x2, USBMINUS;1 [35]
    bld     shift, 4    ;1 [36]
didUnstuff4:
    andi    shift, 0x9f ;1 [37]
    breq    unstuff4    ;1 [38]
    nop2                ;2 [40]
    in      x2, USBIN   ;1 [41] <-- sample bit 5
    eor     x1, x2      ;1 [42]
    bst     x1, USBMINUS;1 [43]
    bld     shift, 5    ;1 [44]
didUnstuff5:
    andi    shift, 0x3f ;1 [45]
    breq    unstuff5    ;1 [46]
    nop2                ;2 [48]
    in      x1, USBIN   ;1 [49] <-- sample bit 6
    eor     x2, x1      ;1 [50]
    bst     x2, USBMINUS;1 [51]
    bld     shift, 6    ;1 [52]
didUnstuff6:
    cpi     shift, 0x02 ;1 [53]
    brlo    unstuff6    ;1 [54]
    nop2                ;2 [56]
    in      x2, USBIN   ;1 [57] <-- sample bit 7
    eor     x1, x2      ;1 [58]
    bst     x1, USBMINUS;1 [59]
    bld     shift, 7    ;1 [60]
didUnstuff7:
    cpi     shift, 0x04 ;1 [61]
    brsh    rxLoop      ;2 [63] loop control
unstuff7:
    andi    x3, ~0x80   ;1 [63]
    ori     shift, 0x80 ;1 [64]
    in      x2, USBIN   ;1 [65] <-- sample stuffed bit 7
    nop                 ;1 [66]
    rjmp    didUnstuff7 ;2 [68]

macro POP_STANDARD ; 12 cycles
    pop     cnt
    pop     x3
    pop     x2
    pop     x1
    pop     shift
    pop     YH
    endm
macro POP_RETI     ; 5 cycles
    pop     YL
    out     SREG, YL
    pop     YL
    endm

#include "asmcommon.inc"

;----------------------------------------------------------------------------
; Transmitting data
;----------------------------------------------------------------------------

txByteLoop:
txBitloop:
stuffN1Delay:                   ;     [03]
    ror     shift               ;[-5] [11] [59]
    brcc    doExorN1            ;[-4]      [60]
    subi    x4, 1               ;[-3]
    brne    commonN1            ;[-2]
    lsl     shift               ;[-1] compensate ror after rjmp stuffDelay
    nop                         ;[00] stuffing consists of just waiting 8 cycles
    rjmp    stuffN1Delay        ;[01] after ror, C bit is reliably clear

sendNakAndReti:                 ;0 [-19] 19 cycles until SOP
    ldi     x3, USBPID_NAK      ;1 [-18]
    rjmp    usbSendX3           ;2 [-16]
sendAckAndReti:                 ;0 [-19] 19 cycles until SOP
    ldi     x3, USBPID_ACK      ;1 [-18]
    rjmp    usbSendX3           ;2 [-16]
sendCntAndReti:                 ;0 [-17] 17 cycles until SOP
    mov     x3, cnt             ;1 [-16]
usbSendX3:                      ;0 [-16]
    ldi     YL, 20              ;1 [-15] 'x3' is R20
    ldi     YH, 0               ;1 [-14]
    ldi     cnt, 2              ;1 [-13]
;   rjmp    usbSendAndReti      fallthrough

; USB spec says:
; idle = J
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles)

;usbSend:
;pointer to data in 'Y'
;number of bytes in 'cnt' -- including sync byte
;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt]
;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction)
usbSendAndReti:
    in      x2, USBDDR          ;[-12] 12 cycles until SOP
    ori     x2, USBMASK         ;[-11]
    sbi     USBOUT, USBMINUS    ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups)
    out     USBDDR, x2          ;[-8] <--- acquire bus
    in      x1, USBOUT          ;[-7] port mirror for tx loop
    ldi     shift, 0x40         ;[-6] sync byte is first byte sent (we enter loop after ror)
    ldi     x2, USBMASK         ;[-5]
    push    x4                  ;[-4]
doExorN1:
    eor     x1, x2              ;[-2] [06] [62]
    ldi     x4, 6               ;[-1] [07] [63]
commonN1:
stuffN2Delay:
    out     USBOUT, x1          ;[00] [08] [64] <--- set bit
    ror     shift               ;[01]
    brcc    doExorN2            ;[02]
    subi    x4, 1               ;[03]
    brne    commonN2            ;[04]
    lsl     shift               ;[05] compensate ror after rjmp stuffDelay
    rjmp    stuffN2Delay        ;[06] after ror, C bit is reliably clear
doExorN2:
    eor     x1, x2              ;[04] [12]
    ldi     x4, 6               ;[05] [13]
commonN2:
    nop                         ;[06] [14]
    subi    cnt, 171            ;[07] [15] trick: (3 * 171) & 0xff = 1
    out     USBOUT, x1          ;[08] [16] <--- set bit
    brcs    txBitloop           ;[09]      [25] [41]

stuff6Delay:
    ror     shift               ;[42] [50]
    brcc    doExor6             ;[43]
    subi    x4, 1               ;[44]
    brne    common6             ;[45]
    lsl     shift               ;[46] compensate ror after rjmp stuffDelay
    nop                         ;[47] stuffing consists of just waiting 8 cycles
    rjmp    stuff6Delay         ;[48] after ror, C bit is reliably clear
doExor6:
    eor     x1, x2              ;[45] [53]
    ldi     x4, 6               ;[46]
common6:
stuff7Delay:
    ror     shift               ;[47] [55]
    out     USBOUT, x1          ;[48] <--- set bit
    brcc    doExor7             ;[49]
    subi    x4, 1               ;[50]
    brne    common7             ;[51]
    lsl     shift               ;[52] compensate ror after rjmp stuffDelay
    rjmp    stuff7Delay         ;[53] after ror, C bit is reliably clear
doExor7:
    eor     x1, x2              ;[51] [59]
    ldi     x4, 6               ;[52]
common7:
    ld      shift, y+           ;[53]
    tst     cnt                 ;[55]
    out     USBOUT, x1          ;[56] <--- set bit
    brne    txByteLoop          ;[57]

;make SE0:
    cbr     x1, USBMASK         ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles]
    lds     x2, usbNewDeviceAddr;[59]
    lsl     x2                  ;[61] we compare with left shifted address
    subi    YL, 2 + 20          ;[62] Only assign address on data packets, not ACK/NAK in x3
    sbci    YH, 0               ;[63]
    out     USBOUT, x1          ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm:
;set address only after data packet was sent, not after handshake
    breq    skipAddrAssign      ;[01]
    sts     usbDeviceAddr, x2   ; if not skipped: SE0 is one cycle longer
skipAddrAssign:
;end of usbDeviceAddress transfer
    ldi     x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag
    USB_STORE_PENDING(x2)       ;[04]
    ori     x1, USBIDLE         ;[05]
    in      x2, USBDDR          ;[06]
    cbr     x2, USBMASK         ;[07] set both pins to input
    mov     x3, x1              ;[08]
    cbr     x3, USBMASK         ;[09] configure no pullup on both pins
    pop     x4                  ;[10]
    nop2                        ;[12]
    nop2                        ;[14]
    out     USBOUT, x1          ;[16] <-- out J (idle) -- end of SE0 (EOP signal)
    out     USBDDR, x2          ;[17] <-- release bus now
    out     USBOUT, x3          ;[18] <-- ensure no pull-up resistors are active
    rjmp    doReturn

 

 

 

 

Last Edited: Fri. Jul 13, 2018 - 11:57 PM