;*************************************************************************** ;* 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 :"USBtoRS232.asm" ;* Title :AVR309:USB to UART protocol converter ;* Date :01.02.2004 ;* Version :2.8 ;* Target MCU :ATmega8 ;* AUTHOR :Ing. Igor Cesko ;* Slovakia ;* address@hidden ;* http://www.cesko.host.sk ;* ;* DESCRIPTION: ;* USB protocol implementation into MCU with noUSB interface: ;* Device: ;* Universal USB interface (3x8-bit I/O port + RS232 serial line + EEPROM) ;* + added RS232 FIFO buffer ;* ;* The timing is adapted for 12 MHz crystal ;* ;* ;* to add your own functions - see section: TEMPLATE OF YOUR FUNCTION ;* ;* to customize device to your company you must change VendorUSB ID (VID) ;* to VID assigned to your company (for more information see www.usb.org) ;* ;*************************************************************************** #ifdef __GNUC__ #define __SFR_OFFSET 0 #define _VECTOR(n) ((n)*2) #include #ifndef __ASSEMBLER__ #error Must be assembled! #endif #define EQ1(adr, val) .equ adr, val #define EQ2(adr, val) equ adr, val #define PMETER(adr, val) adr, val #define DATA_SECTION .data #define PROG_SECTION .text #define EEPROM_SECTION .section .eeprom // @ is not allowed to be the first character for a defined constant #define _X0 \p1 #define HIGH hi8 #define LOW lo8 #define DEF(addr,val) .equ addr, val #define high_rom(x) ((x)>>8) #define low(x) ((x) & 0xFF) #define XL r26 #define XH r27 #define YL r28 #define YH r29 #define ZL r30 #define ZH r31 #define ZH_ZL ZL #define INT0addr SIG_INTERRUPT0 #define URXCaddr SIG_UART_RECV #else .include "m8def.inc" //------------------------------------------------------------ // Defines for AVRASM2 #define EQ1(adr, val) .equ adr = val #define EQ2(adr, val) equ adr = val #define PMETER(adr, val) adr = val #define DATA_SECTION .dseg #define PROG_SECTION .cseg #define EEPROM_SECTION .eseg // @ is not allowed to be the first character for a define constant #define _X0 @0 #define #define DEF(addr,val) .def addr=val #define ZH_ZL ZH:ZL #define byte db #define short dw #endif #ifndef UPM0 #error sadfkj #endif ;comment for AT90S2313 EQ1(UCR,UCSRB) EQ1(UBRR,UBRRL) ;EQ1(EEAR,EEARL) EQ1(USR,UCSRA) ;EQ1(E2END,127) EQ1(RAMEND128,96+127) EQ1(inputport,PINB) EQ1(outputport,PORTB) EQ1(USBdirection,DDRB) EQ1(DATAplus,1) ;signal D+ on PB1 EQ1(DATAminus,0) ;signal D- on PB0 - give on this pin pull-up 1.5kOhm EQ1(USBpinmask,0b11111100) ;mask low 2 bit (D+,D-) on PB EQ1(USBpinmaskDplus,~(1<>8 ;to bitcount CRC polynomial - high byte eor temp1,bitcount ;and make XOR from remains and CRC polynomial - high byte ldi bitcount,CRC16poly&0xFF ;to bitcount CRC polynomial - low byte eor temp0,bitcount ;and make XOR of remainder and CRC polynomial - low byte CRC16NoXOR: dec temp3 ;were already all bits in byte brne CRC16LoopByte ;unless, then go to next bit cp USBBufptrY,ByteCount ;was already end-of-message brne CRC16Loop ;unless then repeat CRC16End: ret ;otherwise finish (in temp0 and temp1 is result) ;------------------------------------------------------------------------------------------ LoadDescriptorFromROM: lpm ;load from ROM position pointer to r0 st Y+,r0 ;r0 save to buffer and increment buffer adiw ZH_ZL,1 ;increment index to ROM dec ByteCount ;till are not all bytes brne LoadDescriptorFromROM ;then load next rjmp EndFromRAMROM ;otherwise finish ;------------------------------------------------------------------------------------------ LoadDescriptorFromROMZeroInsert: lpm ;load from ROM position pointer to r0 st Y+,r0 ;r0 save to buffer and increment buffer bst RAMread,3 ;if bit 3 is one - don't insert zero brtc InsertingZero ;otherwise zero will be inserted adiw ZH_ZL,1 ;increment index to ROM lpm ;load from ROM position pointer to r0 st Y+,r0 ;r0 save to buffer and increment buffer clt ;and clear bld RAMread,3 ;the third bit in RAMread - for to the next zero insertion will be made rjmp InsertingZeroEnd ;and continue InsertingZero: clr r0 ;for insertion of zero st Y+,r0 ;zero save to buffer and increment buffer InsertingZeroEnd: adiw ZH_ZL,1 ;increment index to ROM subi ByteCount,2 ;till are not all bytes brne LoadDescriptorFromROMZeroInsert ;then load next rjmp EndFromRAMROM ;otherwise finish ;------------------------------------------------------------------------------------------ LoadDescriptorFromSRAM: ld r0,Z ;load from position RAM pointer to r0 st Y+,r0 ;r0 save to buffer and increment buffer adiw ZH_ZL,1 ;increment index to RAM dec ByteCount ;till are not all bytes brne LoadDescriptorFromSRAM ;then load next rjmp EndFromRAMROM ;otherwise finish ;------------------------------------------------------------------------------------------ LoadDescriptorFromEEPROM: out EEARL,ZL ;set the address EEPROM Lo out EEARH,ZH ;set the address EEPROM Hi sbi EECR,EERE ;read EEPROM to register EEDR in r0,EEDR ;load from EEDR to r0 st Y+,r0 ;r0 save to buffer and increment buffer adiw ZH_ZL,1 ;increment index to EEPROM dec ByteCount ;till are not all bytes brne LoadDescriptorFromEEPROM;then load next rjmp EndFromRAMROM ;otherwise finish ;------------------------------------------------------------------------------------------ LoadXXXDescriptor: ldi temp0,SOPbyte ;SOP byte sts OutputBufferBegin,temp0 ;to begin of tramsmiting buffer store SOP ldi ByteCount,8 ;8 byte store ldi USBBufptrY,OutputBufferBegin+2 ;to transmitting buffer and RAMread,RAMread ;if will be reading from RAM or ROM or EEPROM brne FromRAMorEEPROM ;0=ROM,1=RAM,2=EEPROM,4=ROM with zero insertion (string) FromROM: rjmp LoadDescriptorFromROM ;load descriptor from ROM FromRAMorEEPROM: sbrc RAMread,2 ;if RAMREAD=4 rjmp LoadDescriptorFromROMZeroInsert ;read from ROM with zero insertion sbrc RAMread,0 ;if RAMREAD=1 rjmp LoadDescriptorFromSRAM ;load data from SRAM rjmp LoadDescriptorFromEEPROM ;otherwise read from EEPROM EndFromRAMROM: sbrc RAMread,7 ;if is most significant bit in variable RAMread=1 clr RAMread ;clear RAMread rcall ToggleDATAPID ;change DATAPID ldi USBBufptrY,OutputBufferBegin+1 ;to transmitting buffer - position of DATA PID ret ;------------------------------------------------------------------------------------------ PrepareUSBOutAnswer: ;prepare answer to buffer rcall PrepareUSBAnswer ;prepare answer to buffer MakeOutBitStuff: inc BitStuffInOut ;transmitting buffer - insertion of bitstuff bits ldi USBBufptrY,OutputBufferBegin ;to transmitting buffer rcall BitStuff mov OutputBufferLength,ByteCount ;length of answer store for transmiting clr BitStuffInOut ;receiving buffer - deletion of bitstuff bits ret ;------------------------------------------------------------------------------------------ PrepareUSBAnswer: ;prepare answer to buffer clr RAMread ;zero to RAMread variable - reading from ROM lds temp0,InputBufferBegin+2 ;bmRequestType to temp0 lds temp1,InputBufferBegin+3 ;bRequest to temp1 cbr temp0,0b10011111 ;if is 5 and 6 bit zero brne VendorRequest ;then this isn't Vendor Request rjmp StandardRequest ;but this is standard Request ;-------------------------- VendorRequest: clr ZH ;for reading from RAM or EEPROM cpi temp1,1 ; brne NoDoSetInfraBufferEmpty ; rjmp DoSetInfraBufferEmpty ;restart infra receiving (if it was stopped by reading from RAM) NoDoSetInfraBufferEmpty: cpi temp1,2 ; brne NoDoGetInfraCode rjmp DoGetInfraCode ;transmit received infra code (if it is in buffer) NoDoGetInfraCode: cpi temp1,3 ; brne NoDoSetDataPortDirection rjmp DoSetDataPortDirection ;set flow direction of datal bits NoDoSetDataPortDirection: cpi temp1,4 ; brne NoDoGetDataPortDirection rjmp DoGetDataPortDirection ;detect of flow direction of data bits NoDoGetDataPortDirection: cpi temp1,5 ; brne NoDoSetOutDataPort rjmp DoSetOutDataPort ;set data bits (if they are inputs, then pull-ups) NoDoSetOutDataPort: cpi temp1,6 ; brne NoDoGetOutDataPort rjmp DoGetOutDataPort ;detect settings of data out bits (if they are input, then pull-ups) NoDoGetOutDataPort: cpi temp1,7 ; brne NoDoGetInDataPort rjmp DoGetInDataPort ;return value of input data port NoDoGetInDataPort: cpi temp1,8 ; brne NoDoEEPROMRead rjmp DoEEPROMRead ;return contents of EEPROM from given address NoDoEEPROMRead: cpi temp1,9 ; brne NoDoEEPROMWrite rjmp DoEEPROMWrite ;write to EEPROM to given address given data NoDoEEPROMWrite: cpi temp1,10 ; brne NoDoRS232Send rjmp DoRS232Send ;transmit byte to serial line NoDoRS232Send: cpi temp1,11 ; brne NoDoRS232Read rjmp DoRS232Read ;returns received byte from serial line NoDoRS232Read: cpi temp1,12 ; brne NoDoSetRS232Baud rjmp DoSetRS232Baud ;set line speed of of serial line NoDoSetRS232Baud: cpi temp1,13 ; brne NoDoGetRS232Baud rjmp DoGetRS232Baud ;return line speed of serial line NoDoGetRS232Baud: cpi temp1,14 ; brne NoDoGetRS232Buffer rjmp DoGetRS232Buffer ;return line speed of serial line NoDoGetRS232Buffer: cpi temp1,15 ; brne NoDoSetRS232DataBits rjmp DoSetRS232DataBits ;set line speed of serial line NoDoSetRS232DataBits: cpi temp1,16 ; brne NoDoGetRS232DataBits rjmp DoGetRS232DataBits ;return line speed of serial line NoDoGetRS232DataBits: cpi temp1,17 ; brne NoDoSetRS232Parity rjmp DoSetRS232Parity ;set line speed of serial line NoDoSetRS232Parity: cpi temp1,18 ; brne NoDoGetRS232Parity rjmp DoGetRS232Parity ;return line speed of serial line NoDoGetRS232Parity: cpi temp1,19 ; brne NoDoSetRS232StopBits rjmp DoSetRS232StopBits ;set line speed of serial line NoDoSetRS232StopBits: cpi temp1,20 ; brne NoDoGetRS232StopBits rjmp DoGetRS232StopBits ;return line speed of serial line NoDoGetRS232StopBits: cpi temp1,USER_FNC_NUMBER+0 ; brne NoDoUserFunction0 rjmp DoUserFunction0 ;execute of user function0 NoDoUserFunction0: cpi temp1,USER_FNC_NUMBER+1 ; brne NoDoUserFunction1 rjmp DoUserFunction1 ;execute of user function1 NoDoUserFunction1: cpi temp1,USER_FNC_NUMBER+2 ; brne NoDoUserFunction2 rjmp DoUserFunction2 ;execute of user function1 NoDoUserFunction2: rjmp ZeroDATA1Answer ;if that it was something unknown, then prepare zero answer ;----------------------------- USER FUNCTIONS -------------------------------------- ;------------------------------TEMPLATE OF YOUR FUNCTION---------------------------- ;------------------ BEGIN: This is template how to write own function -------------- ;free of use are registers: ;temp0,temp1,temp2,temp3,ACC,ZH,ZL ;registers are destroyed after execution (use push/pop to save content) ;at the end of routine you must correctly set registers: ;RAMread - 0=reading from ROM, 1=reading from RAM, 2=reading from EEPROM ;temp0 - number of transmitted data bytes ;ZH,ZL - pointer to buffer of transmitted data (pointer to ROM/RAM/EEPROM) ;to transmit data (preparing data to buffer) : ;to transmit data you must jump to "ComposeEndXXXDescriptor" ;to transmit one zero byte you can jump to "OneZeroAnswer" (commonly used as confirmation of correct processing) ;to transmit two zero byte you can jump to "TwoZeroAnswer" (commonly used as confirmation of error in processing) ;for small size (up to 8 bytes) ansver use buffer AnswerArray (see function DoGetOutDataPort:) DoUserFunctionX: DoUserFunction0: ;send byte(s) of RAM starting at position given by first parameter in function lds temp0,InputBufferBegin+4 ;first parameter Lo into temp0 lds temp1,InputBufferBegin+5 ;first parameter Hi into temp1 ;lds temp2,InputBufferBegin+6 ;second parameter Lo into temp2 ;lds temp3,InputBufferBegin+7 ;second parameter Hi into temp3 ;lds ACC,InputBufferBegin+8 ;number of requested bytes from USB host (computer) into ACC ;Here add your own code: ;------------------------------------------------------------------- nop ;example of code - nothing to do nop nop nop nop ;------------------------------------------------------------------- mov ZL,temp0 ;will be sending value of RAM - from address stored in temp0 (first parameter Lo of function) mov ZH,temp1 ;will be sending value of RAM - from address stored in temp1 (first parameter Hi of function) inc RAMread ;RAMread=1 - reading from RAM ldi temp0,255 ;send max number of bytes - 255 bytes are maximum rjmp ComposeEndXXXDescriptor ;a prepare data DoUserFunction1: rjmp OneZeroAnswer ;only confirm receiving by one zero byte answer DoUserFunction2: rjmp TwoZeroAnswer ;only confirm receiving by two zero bytes answer ;------------------ END: This is template how to write own function ---------------- ;----------------------------- USER FUNCTIONS -------------------------------------- ;-------------------------- DoSetInfraBufferEmpty: rjmp OneZeroAnswer ;acknowledge reception with single zero ;-------------------------- DoGetInfraCode: rjmp OneZeroAnswer ;acknowledge reception with single zero ;-------------------------- DoSetDataPortDirection: lds temp1,InputBufferBegin+7 ;fourth parameter - bit mask - which port(s) to change lds temp0,InputBufferBegin+4 ;first parameter - direction of data bits DDRB andi temp0,0b00111100 ;mask unused pins sbrc temp1,0 ;if bit0 is zero - don't change port state out DDRB,temp0 ;and update direction of data port lds temp0,InputBufferBegin+5 ;second parameter - direction of data bits DDRC sbrc temp1,1 ;if bit1 is zero - don't change port state out DDRC,temp0 ;and update direction of data port lds temp0,InputBufferBegin+6 ;third parameter - direction of data bits DDRD andi temp0,0b11111000 ;mask unused pins ori temp0,0b00000010 ;mask unused pins sbrc temp1,2 ;if bit2 is zero - don't change port state out DDRD,temp0 ;and update direction of data port rjmp OneZeroAnswer ;acknowledge reception with single zero ;-------------------------- DoGetDataPortDirection: in temp0,DDRB ;read direction of DDRB sts AnswerArray,temp0 ;to array AnswerArray in temp0,DDRC ;read direction of DDRC sts AnswerArray+1,temp0 ;to array AnswerArray in temp0,DDRD ;read direction of DDRD sts AnswerArray+2,temp0 ;to array AnswerArray ldi ZL,AnswerArray ;sending is value from AnswerArray ldi temp0,0x81 ;RAMREAD=1 - reading from RAM mov RAMread,temp0 ;(highest bit set to 1 - to zero RAMread immediatelly) ldi temp0,3 ;sending are three bytes rjmp ComposeEndXXXDescriptor ;and prepare data ;-------------------------- DoSetOutDataPort: lds temp1,InputBufferBegin+7 ;fourth parameter - bit mask - which port(s) to change lds temp0,InputBufferBegin+4 ;first parameter - value of data bits PORTB andi temp0,0b00111100 ;mask unused pins sbrc temp1,0 ;if bit0 is zero - don't change port state out PORTB,temp0 ;and update data port lds temp0,InputBufferBegin+5 ;second parameter - value of data bits PORTC sbrc temp1,1 ;if bit1 is zero - don't change port state out PORTC,temp0 ;and update data port lds temp0,InputBufferBegin+6 ;third parameter - value of data bits PORTD andi temp0,0b11111000 ;mask unused pins ori temp0,0b00000011 ;mask unused pins sbrc temp1,2 ;if bit2 is zero - don't change port state out PORTD,temp0 ;and update data port rjmp OneZeroAnswer ;acknowledge reception with single zero ;-------------------------- DoGetOutDataPort: in temp0,PORTB ;read PORTB sts AnswerArray,temp0 ;to array AnswerArray in temp0,PORTC ;read PORTC sts AnswerArray+1,temp0 ;to array AnswerArray in temp0,PORTD ;read PORTD sts AnswerArray+2,temp0 ;to array AnswerArray ldi ZL,AnswerArray ;sending is value from AnswerArray ldi temp0,0x81 ;RAMREAD=1 - reading from RAM mov RAMread,temp0 ;(highest bit set to 1 - to zero RAMread immediatelly) ldi temp0,3 ;sending are three bytes rjmp ComposeEndXXXDescriptor ;and prepare data ;-------------------------- DoGetInDataPort: in temp0,PINB ;read PINB sts AnswerArray,temp0 ;to array AnswerArray in temp0,PINC ;read PINC sts AnswerArray+1,temp0 ;to array AnswerArray in temp0,PIND ;read PIND sts AnswerArray+2,temp0 ;to array AnswerArray ldi ZL,AnswerArray ;sending is value from AnswerArray ldi temp0,0x81 ;RAMREAD=1 - reading from RAM mov RAMread,temp0 ;(highest bit set to 1 - to zero RAMread immediatelly) ldi temp0,3 ;sending are three bytes rjmp ComposeEndXXXDescriptor ;and prepare data ;------------------------------------------------------------------------------------------ DoGetIn: ldi ZL,0 ;sending value in r0 ldi temp0,0x81 ;RAMread=1 - reading from RAM mov RAMread,temp0 ;(highest bit set to 1 - to zero RAMread immediatelly) ldi temp0,1 ;send only single byte rjmp ComposeEndXXXDescriptor ;and prepare data ;------------------------------------------------------------------------------------------ DoEEPROMRead: lds ZL,InputBufferBegin+4 ;first parameter - offset in EEPROM lds ZH,InputBufferBegin+5 ldi temp0,2 mov RAMread,temp0 ;RAMREAD=2 - reading from EEPROM ldi temp0,E2END+1 ;number my byte answers to temp0 - entire length of EEPROM rjmp ComposeEndXXXDescriptor ;otherwise prepare data ;-------------------------- DoEEPROMWrite: lds ZL,InputBufferBegin+4 ;first parameter - offset in EEPROM (address) lds ZH,InputBufferBegin+5 lds r0,InputBufferBegin+6 ;second parameter - data to store to EEPROM (data) out EEAR,ZL ;set the address of EEPROM out EEARH,ZH out EEDR,r0 ;set the data to EEPROM cli ;disable interrupt sbi EECR,EEMWE ;set the master write enable sei ;enable interrupt (next instruction is performed) sbi EECR,EEWE ;write WaitForEEPROMReady: sbic EECR,EEWE ;wait to the end of write rjmp WaitForEEPROMReady ;in loop (max cca 4ms) (because of possible next reading/writing) rjmp OneZeroAnswer ;acknowledge reception with single zero ;-------------------------- DoRS232Send: lds temp0,InputBufferBegin+4 ;first parameter - value transmitted to RS232 out UDR,temp0 ;transmit data to UART WaitForRS232Send: sbis UCR,TXEN ;if disabled UART transmitter rjmp OneZeroAnswer ;then finish - protection because loop lock in AT90S2323/2343 sbis USR,TXC ;wait for transmition finish rjmp WaitForRS232Send rjmp OneZeroAnswer ;acknowledge reception with single zero ;-------------------------- DoRS232Read: rjmp TwoZeroAnswer ;only acknowledge reception with two zero ;-------------------------- DoSetRS232Baud: lds temp0,InputBufferBegin+4 ;first parameter - value of baudrate of RS232 lds temp1,InputBufferBegin+6 ;second parameter - baudrate of RS232 - high byte cbr temp1,1<0 brne AsRequiredGetRS232Buffer ;transmit as many as requested cp ACC,temp0 ;if no requested more that I can send brcc NoShortGetRS232Buffer ;transmit as many as requested AsRequiredGetRS232Buffer: mov temp0,ACC ldi temp1,0 NoShortGetRS232Buffer: subi temp0,2 ;substract word length sbci temp1,0 lds temp2,RS232ReadPosPtr ;obtain index of reading of buffer of RS232 code lds temp3,RS232ReadPosPtr+1 add temp2,temp0 ;obtain where is end adc temp3,temp1 cpi temp3,HIGH(RS232BufferEnd+1) ;if it would overflow brlo ReadNoOverflow ; brne ReadOverflow ;if yes - skip to overflow cpi temp2,LOW(RS232BufferEnd+1) ;otherwise compare LSB brlo ReadNoOverflow ;and do the same ReadOverflow: subi temp2,LOW(RS232BufferEnd+1) ;caculate how many not transfered sbci temp3,HIGH(RS232BufferEnd+1) ;caculate how many not transfered sub temp0,temp2 ;and with this short length of reading sbc temp1,temp3 ;and with this short length of reading ldi temp2,LOW(RS232FIFOBegin) ;and start from zero ldi temp3,HIGH(RS232FIFOBegin) ;and start from zero ReadNoOverflow: lds ZL,RS232ReadPosPtr ;obtain index of reading of buffer of RS232 code lds ZH,RS232ReadPosPtr+1 ;obtain index of reading of buffer of RS232 code sts RS232ReadPosPtr,temp2 ;write new index of reading of buffer of RS232 code sts RS232ReadPosPtr+1,temp3 ;write new index of reading of buffer of RS232 code sbiw ZL,2 ;space for length data - transmitted as first word cbi UCR,RXCIE ;disable interrupt from UART receiving inc RAMread ;RAMread=1 reading from RAM lds temp2,RS232LengthPosPtr lds temp3,RS232LengthPosPtr+1 ;obtain buffer length of RS232 code sub temp2,temp0 ;decrement buffer length sbc temp3,temp1 sts RS232LengthPosPtr,temp2 ;write new buffer length of RS232 code sts RS232LengthPosPtr+1,temp3 sbi UCR,RXCIE ;enable interrupt from UART receiving st Z+,temp2 ;and save real length to packet st Z,temp3 ;and save real length to packet sbiw ZL,1 ;and set to begin inc temp0 ;and about this word increment number of transmited bytes (buffer length) inc temp0 rjmp ComposeEndXXXDescriptor ;and prepare data ;------------------------------------------------------------------------------------------ DoSetRS232DataBits: lds temp0,InputBufferBegin+4 ;first parameter - data bits 0=5db, 1=6db, 2=7db, 3=8db cpi temp0,DataBits8 ;if to set 8-bits communication breq Databits8or9Set ;then don't change 8/9 bit communication in temp1,UCSRB ;otherwise load UCSRB cbr temp1,(1<