📄 mjoy.asm
字号:
;***************************************************************************
;* U S B S T A C K F O R T H E A V R F A M I L Y
;*
;* File Name :"MJoy.asm"
;* Title :MJoy - USB Joystick based on USB stack
;* Date :1.17.2004
;* Version :1.2
;* Target MCU :ATmega8
;* Assembler :Atmel AVR Studio
;* AUTHOR : Mindaugas Milasauskas
;* Lithuania
;* mindaug@mindaugas.com
;* http://www.mindaugas.com
;* Based on code of:
;* Ing. Igor Cesko
;* Slovakia
;* cesko@internet.sk
;* http://www.cesko.host.sk
;*
;* DESCRIPTION:
;* USB protocol implementation into MCU with noUSB interface:
;* Device:
;* MJoy USB Joystick
;*
;* The timing is adapted for 12 MHz crystal
;*
;*
;* Release Notes:
;*
;* v1.2: Second public release
;* 6 axis (4 x 10 bit + 2 x 8 bit), 24 buttons, 1 hat (uses 4 buttons)
;* Added autocalibration
;* Changed layout of HID descriptor
;* Changed USB Vendor ID to make MJoy appear on top of the game devices list
;* Optimized HatSwitch Code
;* Rudder and Throttle axis ar now 10bit
;* Changed axis Dial, Slider to Rx, Z
;* Optimized partially non-blocking ADC reading
;*
;*
;* v1.1: First public release
;* 6 axis - 2x10bit axis + 4x8bit axis
;* 24 buttons
;* 1 hat (uses 4 buttons)
;*
;* v1.0: Very first lab release with 2x10bit axis and 4 buttons
;*
;*
;***************************************************************************
.include "m8def.inc"
.equ UCR =UCSRB
.equ UBRR =UBRRL
.equ EEAR =EEARL
.equ USR =UCSRA
.equ E2END =127
.equ RAMSIZE =159
.equ RAMEND_MJ =255
.equ inputport =PINB
.equ outputport =PORTB
.equ USBdirection =DDRB
.equ DATAplus =1 ;signal D+ on PB1
.equ DATAminus =0 ;signal D- on PB0 - give on this pin pull-up 1.5kOhm
.equ USBpinmask =0b11111100 ;mask low 2 bit (D+,D-) on PB
.equ USBpinmaskDplus =~(1<<DATAplus) ;mask D+ bit on PB
.equ USBpinmaskDminus =~(1<<DATAminus);mask D- bit on PB
.equ SOPbyte =0b10000000 ;Start of Packet byte
.equ DATA0PID =0b11000011 ;PID for DATA0 field
.equ DATA1PID =0b01001011 ;PID for DATA1 field
.equ OUTPID =0b11100001 ;PID for OUT field
.equ INPID =0b01101001 ;PID for IN field
.equ SOFPID =0b10100101 ;PID for SOF field
.equ SETUPPID =0b00101101 ;PID for SETUP field
.equ ACKPID =0b11010010 ;PID for ACK field
.equ NAKPID =0b01011010 ;PID for NAK field
.equ STALLPID =0b00011110 ;PID for STALL field
.equ PREPID =0b00111100 ;PID for FOR field
.equ nSOPbyte =0b00000001 ;Start of Packet byte - reverse order
.equ nDATA0PID =0b11000011 ;PID for DATA0 field - reverse order
.equ nDATA1PID =0b11010010 ;PID for DATA1 field - reverse order
.equ nOUTPID =0b10000111 ;PID for OUT field - reverse order
.equ nINPID =0b10010110 ;PID for IN field - reverse order
.equ nSOFPID =0b10100101 ;PID for SOF field - reverse order
.equ nSETUPPID =0b10110100 ;PID for SETUP field - reverse order
.equ nACKPID =0b01001011 ;PID for ACK field - reverse order
.equ nNAKPID =0b01011010 ;PID for NAK field - reverse order
.equ nSTALLPID =0b01111000 ;PID for STALL field - reverse order
.equ nPREPID =0b00111100 ;PID for FOR field - reverse order
.equ nNRZITokenPID =~0b10000000 ;PID mask for Token packet (IN,OUT,SOF,SETUP) - reverse order NRZI
.equ nNRZISOPbyte =~0b10101011 ;Start of Packet byte - reverse order NRZI
.equ nNRZIDATA0PID =~0b11010111 ;PID for DATA0 field - reverse order NRZI
.equ nNRZIDATA1PID =~0b11001001 ;PID for DATA1 field - reverse order NRZI
.equ nNRZIOUTPID =~0b10101111 ;PID for OUT field - reverse order NRZI
.equ nNRZIINPID =~0b10110001 ;PID for IN field - reverse order NRZI
.equ nNRZISOFPID =~0b10010011 ;PID for SOF field - reverse order NRZI
.equ nNRZISETUPPID =~0b10001101 ;PID for SETUP field - reverse order NRZI
.equ nNRZIACKPID =~0b00100111 ;PID for ACK field - reverse order NRZI
.equ nNRZINAKPID =~0b00111001 ;PID for NAK field - reverse order NRZI
.equ nNRZISTALLPID =~0b00000111 ;PID for STALL field - reverse order NRZI
.equ nNRZIPREPID =~0b01111101 ;PID for FOR field - reverse order NRZI
.equ nNRZIADDR0 =~0b01010101 ;Address = 0 - reverse order NRZI
;status bytes - State
.equ BaseState =0 ;
.equ SetupState =1 ;
.equ InState =2 ;
.equ OutState =3 ;
.equ SOFState =4 ;
.equ DataState =5 ;
;Flags of action
.equ DoNone =0
.equ DoReceiveOutData =1
.equ DoReceiveSetupData =2
.equ DoPrepareOutContinuousBuffer =3
.equ DoReadySendAnswer =4
.equ DoPrepareJoystickAnswer =5
.equ DoReadySendJoystickAnswer =6
; Joystick flags
.equ JoystickDataRequest =1
.equ JoystickDataRequestBit =0
.equ JoystickDataReady =2
.equ JoystickDataReadyBit =1
.equ JoystickDataProcessing =4
.equ JoystickDataProcessingBit =2
.equ JoystickLastDataPID =8
.equ JoystickLastDataPIDBit =3
.equ JoystickReportID =0b00010000
.equ JoystickReportIDBit =4
.equ CRC16poly =0b1000000000000101 ;CRC16 polynomial
.equ Sym10StructSize =12
.equ Sym10EEStructSize =4
.equ Asym8StructSize =8
.equ Asym8EEStructSize =4
.equ XAxisChannel = 0
.equ YAxisChannel = 1
.equ RudderAxisChannel = 2
.equ ThrottleAxisChannel = 3
.equ ZAxisChannel = 4
.equ RxAxisChannel = 5
.equ MAXUSBBYTES =14 ;maximum bytes in USB input message
.equ NumberOfFirstBits =10 ;how many first bits allowed be longer
.equ NoFirstBitsTimerOffset =256-12800*12/1024 ;Timeout 12.8ms (12800us) to terminate after firsts bits
.equ InputBufferBegin =RAMEND_MJ-RAMSIZE ;compare of receiving shift buffer
.equ InputShiftBufferBegin =InputBufferBegin+MAXUSBBYTES ;compare of receiving buffera
.equ OutputBufferBegin =RAMEND_MJ-MAXUSBBYTES-2 ;compare of transmitting buffer
.equ AckBufferBegin =OutputBufferBegin-3 ;compare of transmitting buffer Ack
.equ NakBufferBegin =AckBufferBegin-3 ;compare of transmitting buffer Nak
.equ ConfigByte =NakBufferBegin-1 ;0=unconfigured state
.equ AnswerArray =ConfigByte-8 ;8 byte answer array
.equ JoystickBufferBegin = AnswerArray - MAXUSBBYTES
.equ JoyOutputBufferLength = JoystickBufferBegin - 1
.equ JoyOutBitStuffNumber = JoyOutputBufferLength - 1
.equ BkpOutputBufferLength = JoyOutBitStuffNumber - 1
.equ BkpOutBitStuffNumber = BkpOutputBufferLength - 1
.equ JoyVal = BkpOutBitStuffNumber - 1
.equ ADCChanStarted =JoyVal - 1
.equ ADCValueH =ADCChanStarted - 1
.equ ADCValueL =ADCValueH -1
.equ RegistersBackup = ADCValueL-32
.equ EEPROMFlags = RegistersBackup-1
.equ StackBegin =EEPROMFlags-1 ;low reservoir (stack is big cca 54 byte)
.equ XAxisRAM =RAMEND - Sym10StructSize
.equ YAxisRAM =XAxisRAM - Sym10StructSize
.equ RudderAxisRAM =YAxisRAM - Sym10StructSize
.equ ThrottleAxisRAM =RudderAxisRAM - Sym10StructSize
.equ ZAxisRAM =ThrottleAxisRAM - Asym8StructSize
.equ RxAxisRAM =ZAxisRAM - Asym8StructSize
.def JoystickFlags =R1 ; Endpoint 1 interrupt status flags for joystick reports
.def backupbitcount =R2 ;backup bitcount register in INT0 disconnected
.def RAMread =R3 ;if reading from SRAM
.def backupSREGTimer =R4 ;backup Flag register in Timer interrupt
.def backupSREG =R5 ;backup Flag register in INT0 interrupt
.def ACC =R6 ;accumulator
.def lastBitstufNumber =R7 ;position in bitstuffing
.def OutBitStuffNumber =R8 ;how many bits to send last byte - bitstuffing
.def BitStuffInOut =R9 ;if insertion or deleting of bitstuffing
.def TotalBytesToSend =R10 ;how many bytes to send
.def TransmitPart =R11 ;order number of transmitting part
.def InputBufferLength =R12 ;length prepared in input USB buffer
.def OutputBufferLength =R13 ;length answers prepared in USB buffer
.def MyUpdatedAddress =R14 ;my USB address for update
.def MyAddress =R15 ;my USB address
.def ActionFlag =R16 ;what to do in main program loop
.def temp3 =R17 ;temporary register
.def temp2 =R18 ;temporary register
.def temp1 =R19 ;temporary register
.def temp0 =R20 ;temporary register
.def bitcount =R21 ;counter of bits in byte
.def ByteCount =R22 ;counter of maximum number of received bytes
.def inputbuf =R23 ;receiver register
.def shiftbuf =R24 ;shift receiving register
.def State =R25 ;state byte of status of state machine
;.def XL =R26 ;XL register - pointer to buffer of received IR codes
;.def XH =R27
.def USBBufptrY =R28 ;YL register - pointer to USB buffer input/output
.def ROMBufptrZ =R30 ;ZL register - pointer to buffer of ROM data
;requirements on descriptors
.equ GET_STATUS =0
.equ CLEAR_FEATURE =1
.equ SET_FEATURE =3
.equ SET_ADDRESS =5
.equ GET_DESCRIPTOR =6
.equ SET_DESCRIPTOR =7
.equ GET_CONFIGURATION =8
.equ SET_CONFIGURATION =9
.equ GET_INTERFACE =10
.equ SET_INTERFACE =11
.equ SYNCH_FRAME =12
; Class requests
.equ GET_REPORT =1
.equ GET_IDLE =2
.equ GET_PROTOCOL =3
.equ SET_REPORT =9
.equ SET_IDLE =10
.equ SET_PROTOCOL =11
;Standard descriptor types
.equ DEVICE =1
.equ CONFIGURATION =2
.equ STRING =3
.equ INTERFACE =4
.equ ENDPOINT =5
; Class Descriptor Types
.equ CLASS_HID =0x21
.equ CLASS_Report =0x22
.equ CLASS_Physical =0x23
;databits
.equ DataBits5 =0
.equ DataBits6 =1
.equ DataBits7 =2
.equ DataBits8 =3
;parity
.equ ParityNone =0
.equ ParityOdd =1
.equ ParityEven =2
.equ ParityMark =3
.equ ParitySpace =4
;stopbits
.equ StopBit1 =0
.equ StopBit2 =1
;user function start number
.equ USER_FNC_NUMBER =100
.equ UpdEEThd = 1500 ; 15 seconds to wait before EEPROM write
; this is the threshold determining how many cycles to pass before
; writing axis data to EEPROM. It determines the delay before
; writing which is Delay = CyclePeriod * UpdEEThd
; The structure of Joystick Axis data; All values are offsets
.equ s_UpdateCntH =0 ; Update counter to avoid too many writes to EEPROM
.equ s_UpdateCntL =1
.equ s_EEPROMStructStart =2 ; 16 bit unsigned minimum registered ADC input value
.equ s_MinH =2 ; 16 bit unsigned minimum registered ADC input value
.equ s_MinL =3 ;
.equ s_MaxH =4 ; 16 bit unsigned maximum registered ADC input value
.equ s_MaxL =5 ;
.equ s_CoefH =6 ; 16 bit unsigned asymmetric axis range coeficient
.equ s_CoefL =7
.equ s_NegCoefH =6 ; 16 bit unsigned axis negative range coeficient
.equ s_NegCoefL =7
.equ s_CenterH =8 ; 16 bit unsigned axis center / offset value
.equ s_CenterL =9 ; ( not used for asymmetric axes )
.equ s_PosCoefH =10 ; 16 bit unsigned axis negative range coeficient
.equ s_PosCoefL =11 ; ( not used for asymmetric axes )
;------------------------------------------------------------------------------------------
;********************************************************************
;* ;* Interrupt table
;********************************************************************
.cseg
;------------------------------------------------------------------------------------------
.org 0 ;after reset
rjmp reset
;------------------------------------------------------------------------------------------
.org INT0addr ;external interrupt INT0
rjmp INT0handler
;------------------------------------------------------------------------------------------
.org ERDYaddr
reti
;------------------------------------------------------------------------------------------
;********************************************************************
;* Init program
;********************************************************************
;------------------------------------------------------------------------------------------
reset: ;initialization of processor and variables to right values
ldi temp0,StackBegin ;initialization of stack
out SPL,temp0
clr XH ;RS232 pointer
clr YH ;USB pointer
clr ZH ;ROM pointer
clr JoystickFlags
ldi temp0,JoystickDataReady
or JoystickFlags,temp0
clr MyUpdatedAddress ;new address USB - non-decoded
rcall InitACKBufffer ;initialization of ACK buffer
rcall InitNAKBufffer ;initialization of NAK buffer
rcall InitJoystickBufffer ;initialization of Joystick buffer
rcall USBReset ;initialization of USB addresses
ldi temp0,0b00000100 ;set pull-up on PORTB
out PORTB,temp0
ldi temp0,0b11000000 ;set pull-up on PORTC
out PORTC,temp0
ldi temp0,0b01111011 ;set pull-up on PORTD
out PORTD,temp0
ldi temp0,0b00000000 ;set input on PORTD
out DDRD,temp0
clr temp0 ;
out EEARH,temp0 ;zero EEPROM index
; prepare ADC
ldi temp0, 0b10000110 ;ADC enable
; no start yet
; Single Conversion mode
; no interrupts
;set ADC prescaler to 12MHz / 64 = 187.5kHz
out ADCSRA,temp0
ldi temp0, 0b01000000 ;AVCC refernce, PortC 0 - X
; clear ADLAR for 10 bit conversion
out ADMUX, temp0
;-- initialize all joystick axes
;check if recalibration is needed
ldi temp3, 0
; Read row 1
sbi DDRB,3
cbi PORTB,3
rcall ReadButtonsRow
sbi PORTB,3
cbi DDRB,3 ;return to tristate
add temp3,temp0
; Read row 2
sbi DDRB,4
cbi PORTB,4
rcall ReadButtonsRow
sbi PORTB,4
cbi DDRB,4 ;return to tristate
add temp3,temp0
;Read row 3
sbi DDRB,5
cbi PORTB,5
rcall ReadButtonsRow
sbi PORTB,5
cbi DDRB,5 ;return to tristate
add temp3,temp0
;Read row 4
sbi DDRD,7
cbi PORTD,7
rcall ReadButtonsRow
sbi PORTD,7
cbi DDRC,7 ;return to tristate
add temp3,temp0
tst temp3
brne RecalibNeeded
ldi temp0, 0
rjmp Cont2
RecalibNeeded:
ldi temp0, 1
Cont2:
sts EEPROMFlags, temp0
cbi EECR,EERIE
rcall InitJoystickAxis
ldi temp0,0x0F ;INT0 - respond to leading edge
out MCUCR,temp0 ;
ldi temp0,1<<INT0 ;enable external interrupt INT0
out GIMSK,temp0
;------------------------------------------------------------------------------------------
;********************************************************************
;* Main program
;********************************************************************
sei ;enable interrupts globally
Main:
sbis inputport,DATAminus ;waiting till change D- to 0
rjmp CheckUSBReset ;and check, if isn't USB reset
cpi ActionFlag,DoReceiveSetupData
breq ProcReceiveSetupData
cpi ActionFlag,DoPrepareOutContinuousBuffer
breq ProcPrepareOutContinuousBuffer
sbrc JoystickFlags,JoystickDataRequestBit
rcall ProcJoystickRequest
rjmp Main
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -