Advertisement
Guest User

Untitled

a guest
Oct 10th, 2018
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ; Targeted for the ATtiny25
  2.  
  3. ; NOTE: Since this was developed specifically for communicating with the PCF8574 8-bit I/O expander
  4. ; on a 16x2 LCD display, it only supports I2C Write as Master and sends the address for each byte sent.
  5. ; There is no salve mode, read or multi-byte support
  6.  
  7. ;Pins used
  8. ;---------------------------------
  9. ;
  10. ; ADC3          Physical pin 2 (Thermistor 1)
  11. ; ADC2          Physical pin 3 (Thermistor 2)
  12. ;
  13. ; PortB, 2      Physical pin 7 (Start/Reset button)
  14. ;
  15. ; SCL (Clock)   SCK for the Universal Serial Interface (PORTB, 1) (Physical pin 6)
  16. ; SDA (Data)    SDA for the Universal Serial Interface (PORTB, 0) (Physical pin 5)
  17. ;
  18. ;Registers used
  19. ;---------------------------------
  20. ;
  21. ; R14           I2C Address for SI7021 Humidity/Temperature sensor unit
  22. ; R15           I2C Address for LCD display (7-bit addresses only!)
  23. ; R17           I2C Data byte, bin2dec temp byte
  24. ; R18           Temporary I2C byte, bin2dec input byte
  25. ; R19           I2C bitbang counter, bin2dec temp counter
  26. ; R20           I2C ACK test data, bin2dec temp byte
  27. ; R21           LCD Data byte
  28. ; R22           Temp LCD data byte
  29. ; R23          
  30. ; R24,R25       Delay loop counter (as 16-bit word)
  31. ; R26,R27       Pointer X to SRAM for display data (0x0060 to start)
  32. ; R28,R29       Pointer Y to SRAM for display data copy
  33. ; Data Map
  34. ;---------------------------------
  35. ;
  36. ;
  37.  
  38. ;This file defines aliases for all of the various registers and flags
  39. .include "tn25def.inc"
  40.  
  41.     RJMP    start               ; Skip data segment. This should put the data segment at address 0x02
  42.  
  43.     ; Display buffer. "---" is replaced with actual value based on bin2dec output before sending
  44.     ; the buffer to the LCD via lcd_refresh. use lcd_newbuff to copy this to SRAM first!
  45.     ;        D     B     -     -     -     °     F     _     _     W     B     -     -     -     °     F
  46.     .db     0x44, 0x42, 0x2D, 0x2D, 0x2D, 0xDF, 0x46, 0x20, 0x20, 0x57, 0x42, 0x2D, 0x2D, 0x2D, 0xDF, 0x46
  47.  
  48.     ;        D     P     -     -     -     °     F     _     _     R     H     -     -     -     %    NULL
  49.     .db     0x44, 0x50, 0x2D, 0x2D, 0x2D, 0xDF, 0x46, 0x20, 0x20, 0x52, 0x48, 0x2D, 0x2D, 0x2D, 0x25, 0x00
  50.  
  51.     ; Thermistor calibration tables (Data TBD)
  52.     ;       23°F  32°F  41°F  50°F  59°F  68°F  77°F  86°F  95°F  104°F 113°F 122°F
  53.     ;.db        0x17, 0x20, 0x29, 0x32, 0x3B, 0x44, 0x4D, 0x56, 0x5F, 0x68, 0x71, 0x7A  ; Temp scale (Example data only!)
  54.     ;.db        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  ; Thermistor 1
  55.     ;.db        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  ; Thermistor 2
  56.  
  57.     ;.db        "Initializing...", 0x00 ; String at 0x46
  58.  
  59.     ;.db        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
  60.     ;.db        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00
  61.  
  62.  
  63. start:
  64.     ; General Init
  65.     ;WDR                    ; Watchdog reset
  66.  
  67.     ; Clock init - this plus the fuse setting (lfuse = 0x61) should set the internal clock to ~16MHz)
  68.     LDI     R30, 0x02
  69.     OUT     PLLCSR, R30
  70.  
  71.     ; I/O pin init. the pins used for I2C on PORTB should always be ZERO
  72.     ; DDRB = 1 means the line is LOW (Sinking current, driving line low)
  73.     ; DDRB = 0 means the line is HIGH (High Z, external pullup driving line high)
  74.  
  75.     LDI     R30, (0<<PB1)|(0<<PB3)          ; Set SDA and SCL High (Default state)
  76.     OUT     PORTB, R30 
  77.     LDI     R30, (0<<PB1)|(0<<PB3)
  78.     OUT     DDRB, R30                       ; Enables internal pullup on pin 5 (PORTB, 1) (SDA) and pin 7 (PORTB, 3) (SCL)
  79.  
  80.     LDI     R16, 0x3F                       ; 0x3F is the I2C address of the LCD
  81.     MOV     R15, R16                        ; Stash the address in R15
  82.  
  83.     LDI     R16, 0x40                       ; 0x40 is the I2C address of the SI7021 sensor
  84.     MOV     R14, R16                        ; Stash the address in R14
  85.  
  86.     RCALL   long_delay                      ; Per the 1620 LCD controller datasheet, we need to wait at least 30ms before talking to it...
  87.     RCALL   long_delay                      ;
  88.     RCALL   lcd_init                        ; LCD is initialized!
  89.    
  90.  
  91.     ; Ready to begin out actual program! Anything below is just test stuff...
  92. ;***************************************************************************************
  93. ;***************************************************************************************
  94. ;***************************************************************************************
  95. ;   LDI     R26, LOW(7150)
  96. ;   MOV     R2, R26
  97. ;   LDI     R26, HIGH(7150)
  98. ;   MOV     R3, R26
  99. ;
  100. ;   LDI     R26, 0x62                       ; Buffer line 1 starts at 0x0060
  101. ;   LDI     R27, 0x00                       ;
  102. ;   RCALL   bin2dec16                       ; bin2dec16 is written to use R3:R2 as the input
  103. ;   RJMP    trap
  104.  
  105. main:
  106.     RCALL   lcd_newbuff
  107.  
  108.     ; Read humidity...
  109.     ; Read temperature...
  110.     MOV     R16, R14                        ; I2C address for sensor
  111.     LDI     R17, 0xF5                       ; Command: Read humidity, no master hold
  112.     RCALL   i2c_senddata                    ; Send command to read temperature
  113.  
  114.     ; R16 will change from the value in R14 to 0 when data is received
  115.     h_convert_wait:                         ; Attempt to read the data.
  116.         RCALL   long_delay
  117.         RCALL   i2c_getdata
  118.         CP      R14, R16
  119.         BREQ    h_convert_wait
  120.  
  121.     ; From the 16-bit value in R5:R4 we calculate the relative humidity in percent by:
  122.     ;       RH% = ((125 * RH_Code) / 65536) - 6
  123.     ;
  124.     ; To preserve decimal places we multiply by 100:
  125.     ;       RH% * 100 = ((12500 * RH_Code) / 65536) - 600
  126.     ;
  127.     ; Then, when we convert this value to ASCII for display, we effectively divide by 100
  128.     ; by dropping the last two digits and rounding
  129.  
  130.     ; Step 1: Multiply sensor data by 12500 (0x30D4)
  131.     ; R4, R5, R6, R7        16-bit multiplicand input (32-bit extended) - Value is already loaded from the i2c_getdata routine
  132.     ; R24, R25              16-bit multiplier
  133.     LDI     R24, 0xD4
  134.     LDI     R25, 0x30
  135.  
  136.     RCALL   multiply16                      ; Result is in R3:R2:R1:R0. We divide by 65536 by simply discarding the low 16-bits (R1:R0).
  137.                                             ; So the final result is in R3:R2
  138.     LDI     R16, 0x58                      
  139.     LDI     R17, 0x02
  140.     SUB     R2, R16                         ; Subtract 600 (0x0258)
  141.     SBC     R3, R17                         ; RH * 100 is now in R3:R2. Convert this to ASCII and put it into the memory buffer for the display!
  142.  
  143.     LDI     R26, 0x7B                       ; "RH" in buffer buffer line 1 starts at 0x0078
  144.     LDI     R27, 0x00                       ;
  145.     RCALL   bin2dec16                       ; bin2dec16 is written to use R3:R2 as the input
  146.  
  147.  
  148.  
  149.  
  150.     ; Read temperature...
  151.     MOV     R16, R14                        ; I2C address for sensor
  152.     LDI     R17, 0xE0                       ; Command: Read temp from previous RH measurement
  153.     RCALL   i2c_senddata                    ; Send command to read temperature
  154.  
  155.     ; R16 will change from the value in R14 to 0 when data is received
  156.     t_convert_wait:                         ; Attempt to read the data.
  157.         RCALL   long_delay
  158.         RCALL   i2c_getdata
  159.         CP      R14, R16
  160.         BREQ    t_convert_wait
  161.        
  162.                                
  163.     ; From the 16-bit value in R5:R4 we calculate the temperature in degrees C by:
  164.     ;       Temp (deg. C) = ((175.72 * Temp_Code) / 65536) - 46.85
  165.     ;
  166.     ; To preserve decimal places we multiply by 100:
  167.     ;       Temp (deg. C) * 100 = ((17572 * Temp_Code) / 65536) - 4685
  168.     ;
  169.     ; Then, when we convert this value to ASCII for display, we effectively divide by 100
  170.     ; by dropping the last two digits and rounding
  171.  
  172.     ; Step 1: Multiply sensor data by 17572 (0x44A4)
  173.     ; R4, R5, R6, R7        16-bit multiplicand input (32-bit extended) - Value is already loaded from the i2c_getdata routine
  174.     ; R24, R25              16-bit multiplier
  175.  
  176.     LDI     R24, 0xA4
  177.     LDI     R25, 0x44
  178.  
  179.     RCALL   multiply16                      ; Result is in R3:R2:R1:R0. We divide by 65536 by simply discarding the low 16-bits (R1:R0).
  180.                                             ; So the final result is in R3:R2
  181.     LDI     R16, 0x4D
  182.     LDI     R17, 0x12
  183.     SUB     R2, R16                         ; Subtract 4685 (0x124D)
  184.     SBC     R3, R17                         ; Temp * 100 is now in R3:R2. Convert this to ASCII and put it into the memory buffer for the display!
  185.  
  186.     ; Convert temp from C to F.
  187.  
  188.     ; Copy value from R3:R2 to R19:R18 to save for later
  189.     MOV     R18, R2
  190.     MOV     R19, R3
  191.  
  192.     ; To convert C to F, we multiply by 9/5, or 1.8. This sets up multiplying by 0.8
  193.     LDI     R24, LOW(52429)                 ; Multiplier
  194.     LDI     R25, HIGH(52429)
  195.     MOV     R4, R2                          ; Multiplicand (value in C)
  196.     MOV     R5, R3
  197.     CLR     R6
  198.     CLR     R7
  199.    
  200.     RCALL   multiply16
  201.  
  202.     ; Result is in R3:R2. Add the original value saved in R19:R18 for effectively multiplying by 9/5
  203.     ADD     R2, R18
  204.     ADC     R3, R19
  205.     LDI     R18, LOW(3200)
  206.     LDI     R19, HIGH(3200)
  207.     ADD     R2, R18                         ; Add 32. Remember this value is effectively multiplied by 100 as well!
  208.     ADC     R3, R19
  209.  
  210.     ; Done reading temperature! Convert the result and put it into the display buffer...
  211.     LDI     R26, 0x62                       ; Buffer line 1 starts at 0x0060
  212.     LDI     R27, 0x00                       ;
  213.     RCALL   bin2dec16                       ; bin2dec16 is written to use R3:R2 as the input
  214.  
  215.  
  216.  
  217.     RCALL   lcd_refresh                     ; Update dispaly!
  218.     RJMP    main                            ; Loop
  219.  
  220. trap:                                       ; Trap loop for debugging - erase later?
  221.     RJMP    trap
  222.  
  223.  
  224. ;***********************************************************************************************************************
  225. ; multiply16
  226. ;***********************************************************************************************************************
  227. ; 16-bit multiplication, returns 32-bit value in R3:R2:R1:R0
  228. ;
  229. ;
  230. ; R4, R5, R6, R7        16-bit multiplicand input (32-bit extended)
  231. ; R0, R1, R2, R3        32-bit output. FINAL output will be in 16-bit word R3:R2
  232. ; R23                   Shift counter (=16 to start)
  233. ; R24, R25              16-bit multiplier
  234. ;
  235. multiply16:
  236.  
  237.     CLR     R0          ; Set output to 0.
  238.     CLR     R1
  239.     CLR     R2
  240.     CLR     R3
  241.     LDI     R23, 16     ; Shift counter
  242.  
  243. multloop:
  244.     MOV     R8, R25     ; Test if the multiplier is zero
  245.     OR      R8, R24     ;
  246.     BREQ    multend     ; Exit if multiplier is 0. This leaves the ouput as 0 as well.
  247.  
  248.     SBRS    R24, 0      ; Is the LSB of R25:R24 one?
  249.     RJMP    noadd       ; If it's zero, skip this next part...
  250.  
  251.     ; Add R19:R18:R17:R16 to output
  252.     ADD     R0, R4      ; Add first bytes
  253.     ADC     R1, R5      ; Add with carry second bytes
  254.     ADC     R2, R6      ; Add with carry third bytes
  255.     ADC     R3, R7      ; Add with carry fourth bytes
  256.  
  257. noadd:
  258.     ; Rotate multiplicand left through carry
  259.     CLC                 ; Clear carry first so we don't contaminate our data
  260.     ROL     R4          ; Shift first byte through carry
  261.     ROL     R5          ; Shift second byte through carry
  262.     ROL     R6          ; Shift third byte through carry
  263.     ROL     R7          ; Shift fourth byte through carry
  264.  
  265.     ; Rotate multiplier right (no carry)
  266.     CLC                 ; Clear carry first so we don't contaminate our data
  267.     ROR     R25         ; Shift second byte through carry
  268.     ROR     R24         ; Shift first byte through carry
  269.  
  270.     DEC     R23         ; Decrement counter
  271.     BREQ    multend     ; If counter is zero, we're done.  
  272.  
  273.     RJMP    multloop    ; Next step of the multiplication process
  274.  
  275. multend:
  276.  
  277.     RET
  278.  
  279.  
  280.  
  281. ;***********************************************************************************************************************
  282. ; BIN2DEC16
  283. ;***********************************************************************************************************************
  284. ; Converts a 16-bit byte in R3:R2 to three-char (XXX) ASCII string (non-terminated).
  285. ; Expects the input value to be actual value multiplied by 100, so it effectively divides by 100
  286. ; by dropping the last two digits and rounding.
  287. ;
  288. ; Input R3:R2
  289. ; SRAM address preloaded into register X (R27:R26)
  290.  
  291. bin2dec16:
  292.     LDI     R20, 0x30           ; Load 0x30 (48, ASCII "0") into counter R20
  293.  
  294.     LDI     R16, LOW(10000)     ; Load the value 10,000 into 16-bit word R17:R16
  295.     LDI     R17, HIGH(10000)    ;
  296.  
  297.     MOV     R10, R2             ; Make a copy of the value to be converted
  298.     MOV     R11, R3
  299.  
  300.     K10000_16:
  301.         CP      R10, R16            ; Compare the current value (R11:R10) to what we're subtractiong (R17:R16)
  302.         CPC     R11, R17            ; and if equal jump to the next decade
  303.         BRLO    STK10000_16
  304.         SUB     R10, R16            ; 16-bit Subtract
  305.         SBC     R11, r17
  306.         INC     R20                 ; Add 1 to counter
  307.         RJMP    k10000_16           ; Loop
  308.  
  309.     STK10000_16:                   
  310.         MOV     R4, R20             ; Make a copy of the 10,000s place for later
  311.         LDI     R20, 0x30           ; Reload 0x30 (48, ASCII "0") into counter R20
  312.         LDI     R16, LOW(1000)      ; Load the value 1,000 into 16-bit word R17:R16
  313.         LDI     R17, HIGH(1000)
  314.  
  315.     K1000_16:
  316.         CP      R10, R16            ; Compare the current value (R11:R10) to what we're subtractiong (R17:R16)
  317.         CPC     R11, R17            ; and if equal jump to the next decade
  318.         BRLO    STK1000_16
  319.         SUB     R10, R16            ; 16-bit Subtract
  320.         SBC     R11, r17
  321.         INC     R20                 ; Add 1 to counter
  322.         RJMP    k1000_16            ; Loop
  323.  
  324.     STK1000_16:                
  325.         MOV     R5, R20             ; Make a copy of the 1,000s place for later
  326.         LDI     R20, 0x30           ; Reload 0x30 (48, ASCII "0") into counter R20
  327.         LDI     R16, LOW(100)       ; Load the value 100 into 16-bit word R17:R16
  328.         LDI     R17, HIGH(100)
  329.  
  330.     K100_16:
  331.         CP      R10, R16            ; Compare the current value (R11:R10) to what we're subtractiong (R17:R16)
  332.         CPC     R11, R17            ; and if equal jump to the next decade
  333.         BRLO    STK100_16
  334.         SUB     R10, R16            ; 16-bit Subtract
  335.         SBC     R11, r17
  336.         INC     R20                 ; Add 1 to counter
  337.         RJMP    k100_16             ; Loop
  338.  
  339.     STK100_16:                 
  340.         MOV     R6, R20             ; Make a copy of the 100s place for later
  341.         CLR     R20
  342.         LDI     R16, LOW(10)        ; Load the value 10 into 16-bit word R17:R16
  343.         LDI     R17, HIGH(10)
  344.  
  345.     K10_16:
  346.         CP      R10, R16            ; Compare the current value (R11:R10) to what we're subtractiong (R17:R16)
  347.         CPC     R11, R17            ; and if equal jump to the next decade
  348.         BRLO    STK10_16
  349.         SUB     R10, R16            ; 16-bit Subtract
  350.         SBC     R11, r17
  351.         INC     R20                 ; Add 1 to counter
  352.         RJMP    k10_16              ; Loop
  353.  
  354.     STK10_16:
  355.         CPI     R20, 0x05           ; If the 10s place is 5 or more, round the 100s place up by incrementing
  356.         BRLO    skip_round          ; If not, skip right to storage
  357.         INC     R6                  ; so if the remainder >=5 we add 1 to the 100s place and continue rounding
  358.  
  359.         LDI     R20, 0x30           ; Reload 0x30 (48, ASCII "0") into counter R20
  360.         LDI     R21, 0x3A           ; Value for comparing
  361.  
  362.         CP      R6, R21             ; If the 100s place is 10, carry the rounding through to the 1,000s place
  363.         BRLO    skip_round
  364.         MOV     R6, R20             ; Set 100s place to "0"
  365.         INC     R5                  ; Increment the 1,000s place
  366.  
  367.         CP      R5, R21             ; If the 1,000s place is 10, carry the rounding through to the 10,000s place
  368.         BRLO    skip_round
  369.         MOV     R5, R20             ; Set 1,000s place to "0"
  370.         INC     R4                  ; Increment the 10,000s place
  371.  
  372.     skip_round:
  373.         ; Done rounding, let's store the value in SRAM
  374.         ST      X+, R4              ; 10,000s place
  375.         ST      X+, R5              ;  1,000s place
  376.         ST      X, R6               ;    100s place
  377.  
  378.     ; Now let's remove leading zeros! It's just easier to do this as an postprocess...
  379.     SUBI    R26, 0x02               ; Return to the original X location by substracting 2
  380.     LDI     R20, 0x20               ; Load " " into R20
  381.     LD      R16, X                  ; Load value from SRAM
  382.     CPI     R16, 0x30               ; Is the value "0" ?
  383.     BRNE    bin2dec16_done          ; If not, don't bother checking anything else
  384.     ST      X+, R20                 ; If it is, replace with " "
  385.     LD      R16, X                  ; Load value from SRAM
  386.     CPI     R16, 0x30               ; Is the value "0" ?
  387.     BRNE    bin2dec16_done          ; If not, don't bother checking anything else
  388.     ST      X, R20                  ; If it is, replace with " "
  389.    
  390.     ; By only doing this twice, we leave a zero before the decimal place and any zeros after the decimal place
  391.     bin2dec16_done:
  392.     RET
  393.  
  394.  
  395.  
  396.  
  397. ;***********************************************************************************************************************
  398. ; LCD Refresh
  399. ;***********************************************************************************************************************
  400. ; Updates the LCD display with the data from the buffer pointed at by the X register
  401. ; (R27:R26). Expects a null terminated string. Will automatically split the string into
  402. ; 2 lines if necessary.
  403.  
  404. lcd_refresh:
  405.  
  406.     LDI     R26, 0x60           ; Buffer starts at 0x0060
  407.     LDI     R27, 0x00           ;
  408.  
  409.     LDI     R21, 0x80           ; Move to start of row 1 at address DDRAM 0x00
  410.     RCALL   lcd_sendinstr       ; Command 0x80 = Instruction 0x80 + Address 0x00
  411.  
  412.     LDI     R24, 0x10           ; Copy up to 16 bytes from buffer to LCD
  413.  
  414.     refresh1:
  415.         LD      R21, X+         ; Load byte from SRAM (X pointer)
  416.         TST     R21             ; Test R21 for zero (or minus, which would be invalid)
  417.         BREQ    refresh_done    ; If it was zero, skip to the end
  418.         MOV     R16, R15        ; Load I2C address for LCD
  419.         RCALL   lcd_senddata    ; Send byte to LCD display
  420.         DEC     R24             ; Count down
  421.         BRNE    refresh1        ; If counter isn't 0, loop back
  422.    
  423.     LDI     R21, 0xC0           ; Move to start of row 2 at address DDRAM 0x40
  424.     RCALL   lcd_sendinstr       ; Command 0xC0 = Instruction 0x80 + Address 0x40
  425.  
  426.     LDI     R24, 0x10           ; Copy up to another 16 bytes from buffer to LCD
  427.  
  428.     refresh2:
  429.         LD      R21, X+         ; Load byte from SRAM (X pointer)
  430.         TST     R21             ; Test R21 for zero (or minus, which would be invalid)
  431.         BREQ    refresh_done    ; If it was zero, skip to the end
  432.         MOV     R16, R15        ; Load I2C address for LCD
  433.         RCALL   lcd_senddata    ; Send byte to LCD display
  434.         DEC     R24             ; Count down
  435.         BRNE    refresh2        ; If counter isn't 0, loop back
  436.  
  437.     refresh_done:
  438.         RET
  439.  
  440. ;***********************************************************************************************************************
  441. ; LCD New Buffer
  442. ;***********************************************************************************************************************
  443. ; Copies the primary LCD default buffer data from program memory to SRAM.
  444. ; Useful in case you trample the LCD buffer with some other message...
  445. lcd_newbuff:
  446.     LDI     R30, 0x02           ; Data starts at program memory 0x0002
  447.     CLR     R31                 ; High byte of pointer needs to be 0x00
  448.     LDI     R28, 0x60           ; Copy data to SRAM which starts at 0x0060
  449.     CLR     R29                 ; High byte of pointer needs to be zero
  450.     LDI     R17, 0x20           ; Copy 32 bytes total
  451.     buffcopy:
  452.         LPM     R18, Z+         ; Copy byte from program memory into register R18, then increment pointer
  453.         ST      Y+, R18         ; Copy register R18 into SRAM, then increment pointer
  454.         DEC     R17             ; Count down
  455.         BRNE    buffcopy        ; If counter isn't 0, loop back
  456.     RET
  457.  
  458. ;***********************************************************************************************************************
  459. ; LCD Initialize
  460. ;***********************************************************************************************************************
  461. ; Sends the required LCD initialization sequence
  462. ;
  463. lcd_init:
  464.     ; Not sure why, but sending the high nybble of the first init byte
  465.     ; twice makes operation more stable and consistent...
  466.     ; The datasheet actually specifies this explicitly, too, without explaination
  467.     LDI     R17, 0x24
  468.     MOV     R16, R15            ; Load I2C address for LCD
  469.     RCALL   i2c_senddata
  470.     LDI     R17, 0x20
  471.     RCALL   i2c_senddata
  472.    
  473.     LDI     R21, 0x2C
  474.     RCALL   lcd_sendinstr
  475.  
  476.     LDI     R21, 0x0C
  477.     RCALL   lcd_sendinstr
  478.  
  479.     LDI     R21, 0x01
  480.     RCALL   lcd_sendinstr
  481.  
  482.     RCALL   long_delay      ;(pause)
  483.  
  484.     LDI     R21, 0x06
  485.     RCALL   lcd_sendinstr  
  486.  
  487.     RET
  488.  
  489.  
  490. ;***********************************************************************************************************************
  491. ; Send LCD instruction byte
  492. ;***********************************************************************************************************************
  493. ; This routine takes a byte in register R21, splits it into two nybbles and sends
  494. ; it to the LCD INSTRUCTION register using 4 commands
  495. ;
  496. lcd_sendinstr:
  497.     MOV     R22, R21            ; Make a copy of the byte
  498.     SWAP    R22                 ; Swap high and low nybbles
  499.     LDI     R17, 0xF0           ; Use R17 as a temp register
  500.     AND     R21, R17            ; Mask lower nybble of copy 1 (High nybble of original)
  501.     AND     R22, R17            ; Mask lower nybble of copy 2 (Low nybble of original)
  502.     MOV     R16, R15            ; Load I2C address for LCD
  503.  
  504.     ;Send high nybble with 0x04 and then 0x00 low nybble to load INSTRUCTION register
  505.     MOV     R17, R21
  506.     ORI     R17, 0x04           ; High nybble + 0x04
  507.     RCALL   i2c_senddata
  508.     MOV     R17, R21
  509.     ORI     R17, 0x00           ; High nybble + 0x00
  510.     RCALL   i2c_senddata
  511.  
  512.     ;Send low nybble with 0x04 and then 0x00 low nybble
  513.     MOV     R17, R22
  514.     ORI     R17, 0x04           ; High nybble + 0x04
  515.     RCALL   i2c_senddata
  516.     MOV     R17, R22
  517.     ORI     R17, 0x00           ; High nybble + 0x00
  518.     RCALL   i2c_senddata
  519.  
  520.     RET
  521.  
  522. ;***********************************************************************************************************************
  523. ; Send LCD data byte
  524. ;***********************************************************************************************************************
  525. ; This routine takes a byte in register R21, splits it into two nybbles and sends
  526. ; it to the LCD DATA register using 4 commands
  527. ;
  528. lcd_senddata:
  529.     MOV     R22, R21            ; Make a copy of the byte
  530.     SWAP    R22                 ; Swap high and low nybbles
  531.     LDI     R17, 0xF0           ; Use R17 as a temp register
  532.     AND     R21, R17            ; Mask lower nybble of copy 1 (High nybble of original)
  533.     AND     R22, R17            ; Mask lower nybble of copy 2 (Low nybble of original)
  534.     MOV     R16, R15            ; Load I2C address for LCD
  535.  
  536.     ;Send high nybble with 0x0D and then 0x09 low nybble to load DATA register
  537.     MOV     R17, R21
  538.     ORI     R17, 0x0D           ; High nybble + 0x0D
  539.     RCALL   i2c_senddata
  540.     MOV     R17, R21
  541.     ORI     R17, 0x09           ; High nybble + 0x09
  542.     RCALL   i2c_senddata
  543.  
  544.     ;Send low nybble with 0x04 and then 0x00 low nybble
  545.     MOV     R17, R22
  546.     ORI     R17, 0x0D           ; High nybble + 0x0D
  547.     MOV     R16, R15            ; Copy LCD address to I2C address register
  548.     RCALL   i2c_senddata
  549.     MOV     R17, R22
  550.     ORI     R17, 0x09           ; High nybble + 0x09
  551.     MOV     R16, R15            ; Copy LCD address to I2C address register
  552.     RCALL   i2c_senddata
  553.  
  554.     RET
  555.  
  556. ;***********************************************************************************************************************
  557. ; I2C send data
  558. ;***********************************************************************************************************************
  559. ; -Send start condition
  560. ; -Send I2C address from register R16
  561. ; -Wait for ACK for 2 x Standard Delay period
  562. ;    if no ACK then return with error (How?)
  563. ; -Send I2C data from register R17
  564. ; -Wait for ACK for 2 x Standard Delay period
  565. ;    if no ACK then return with error (How?)
  566. ;
  567.  
  568. ; DDRB = 1 means the line is LOW (Sinking current, driving line low)
  569. ; DDRB = 0 means the line is HIGH (High Z, external pullup driving line high)
  570. i2c_senddata:
  571.     MOV     R18, R16            ; Copy of I2C address
  572.     LSL     R18                 ; Pre-shift the address byte one bit. LSB = 0 indicating write
  573.     RCALL   i2c_start           ; I2C start condition
  574.     RCALL   i2c_sendbyte        ; Sends contents of R18 (address + write)
  575.     SBRS    R20, 0              ; Re-test if SDA was high
  576.     RJMP    i2c_nextbyte        ; If not, we have ACK! Jump to the next byte
  577.     RJMP    i2c_stop            ; If bit 0 of R20 is set, we have no ACK. Stop the transmission.
  578.  
  579.     i2c_nextbyte:               ; We get here if there was a successful ACK - send the data byte!
  580.         RCALL   i2c_delay       ; Longer delay between address and data bytes
  581.         RCALL   i2c_delay
  582.         RCALL   i2c_delay
  583.  
  584.         MOV     R18, R17        ; Make a copy of the data byte
  585.         RCALL   i2c_sendbyte    ; Send the data byte (without preshifting)
  586.        
  587.         RJMP    i2c_stop        ; Even if there wasn't an ACK, we're done here.
  588.     RET                         ; Just in case? (i2c_stop has a RET in it)
  589.        
  590. ;***********************************************************************************************************************
  591. ; I2C send byte
  592. ;***********************************************************************************************************************
  593. ; -Sends 8 bits from register R18
  594. ; -Wait for ACK for 2 x Standard Delay period
  595. ;    if no ACK then return with error (How?)
  596. i2c_sendbyte:
  597.     LDI     R19, 0x08           ; Load the bit counter to produce 8 bit pulses (8 bits of data)
  598.  
  599.     i2c_bitbang:                ; Begin bitbang loop!
  600.         SBI     DDRB, 1         ; Set SCL Low
  601.         SBRC    R18, 7          ; Is MSB of R18 low?
  602.         CBI     DDRB, 0         ; If not, set SDA high
  603.         SBRS    R18, 7          ; If yes, is MSB of R18 high? (Better be!)
  604.         SBI     DDRB, 0         ; If not, set SDA low
  605.         RCALL   i2c_delay       ; Delay
  606.         CBI     DDRB, 1         ; Set SCL high
  607.         RCALL   i2c_delay       ; Delay
  608.         LSL     R18             ; Shift the data left by 1 bit
  609.         DEC     R19             ; Decrement counter
  610.         BRNE    i2c_bitbang     ; If counter is zero, we're done!
  611.  
  612.                                 ; Process ACK.
  613.     SBI     DDRB, 1             ; Set SCL Low
  614.     RCALL   i2c_delay           ; Delay to give slace time to ack
  615.     CBI     DDRB, 0             ; Set SDA high
  616.     RCALL   i2c_delay           ; Delay
  617.     CBI     DDRB, 1             ; Set SCL high. Slave device should pull SDA low now
  618.     RCALL   i2c_delay           ; Delay
  619.  
  620.     LDI     R19, 0xF0           ; A small delay only
  621.  
  622.     i2c_ack_test:
  623.         IN      R20, PINB       ; Input from Port B.
  624.         SBRS    R20, 0          ; Is SDA still high?
  625.         RJMP    i2c_ack_ok      ; If not, we have ACK! Exit the test loop
  626.         DEC     R19             ; Decrement counter
  627.         BRNE    i2c_ack_test    ; Keep testing for a bit
  628.                                 ; If we make it here, there was no ACK.
  629.     i2c_ack_ok:
  630.     RET
  631.  
  632.  
  633.  
  634. ;***********************************************************************************************************************
  635. ; I2C get data
  636. ;***********************************************************************************************************************
  637. ; -Generate start condition
  638. ; -Send I2C address from register R16
  639. ; -Wait for ACK for 2 x Standard Delay period
  640. ;    if no ACK then return with error (How?)
  641. ; -Pull 8 bits I2C data into R5
  642. ; -Generate ACK
  643. ; -Pull 8 bits I2C data into R4
  644. ; -Generate NACK (This prevents transmission of checksum byte)
  645. ; -Generate stop condition
  646.  
  647. i2c_getdata:
  648.     MOV     R18, R16            ; Make a copy of the address byte
  649.     LSL     R18                 ; Pre-shift the address byte one bit
  650.     ORI     R18, 1              ; Set the LSB, indicating a read operation
  651.     RCALL   i2c_start           ; I2C Start condition
  652.     RCALL   i2c_sendbyte        ; Send the byte in R18 (Address + Read)
  653.     IN      R20, PINB           ; Input from Port B.
  654.     SBRS    R20, 0              ; Test if SDA is high.
  655.     RJMP    i2c_getstart        ; If not, we have ACK! Jump to the next byte
  656.     RJMP    i2c_stop            ; If bit 0 of R20 is set, we have no ACK.
  657.  
  658.  
  659.     i2c_getstart:
  660.         LDI     R17, 0x02       ; Get 2 bytes of data
  661.         CLR     R4              ; Clear R7:R6:R5:R4 for receiving data
  662.         CLR     R5              ; Only R5:R4 are loaded with new data, though
  663.         CLR     R6              ;
  664.         CLR     R7              ;
  665.  
  666.     i2c_getbyte:                ;
  667.         RCALL   i2c_delay       ; Longer delay between address and data bytes
  668.         RCALL   i2c_delay
  669.         RCALL   i2c_delay
  670.  
  671.         LDI     R19, 0x08       ; Load the bit counter to produce 8 bit pulses (8 bits of data)
  672.         SBI     DDRB, 1         ; Set SCL Low
  673.         CBI     DDRB, 0         ; Set SDA high (slave will pull it low)
  674.  
  675.     i2c_bitslurp:               ; Begin bitslurp loop!
  676.         SBI     DDRB, 1         ; Set SCL Low
  677.         RCALL   i2c_delay       ; Delay
  678.         CBI     DDRB, 1         ; Set SCL high
  679.         IN      R20, PINB       ; Input from Port B.
  680.         ROR     R20             ; We're using PB0 for SDA to the input is the LSB only...
  681.         ROL     R4              ; Rotate the LSB from R20 into carry and back out into R3, and MSB from R3 into carry
  682.         ROL     R5              ; Rotate the MSB of R3 from carry back out into R4.
  683.         DEC     R19             ; Decrement counter (read 8 bits)
  684.         BRNE    i2c_bitslurp    ; If counter is zero, we're done!
  685.  
  686.     DEC     R17                 ; We're reading two bytes
  687.     BREQ    i2c_getdone         ; If counter is zero, we're done!
  688.  
  689.                                 ; Produce an ACK by holding SDA low and pulsing the clock
  690.     SBI     DDRB, 1             ; SCL Low
  691.     SBI     DDRB, 0             ; SDA Low
  692.     RCALL   i2c_delay           ; Delay
  693.     CBI     DDRB, 1             ; Set SCL high
  694.     RCALL   i2c_delay           ; Delay
  695.  
  696.     RJMP    i2c_getbyte         ; Get the next byte
  697.  
  698.  
  699.     i2c_getdone:
  700.                             ; Produce a NACK (because the datasheet says to?) by releasing SDA and pulsing the clock
  701.     SBI     DDRB, 1         ; SCL Low
  702.     SBI     DDRB, 1         ; SDA high
  703.     RCALL   i2c_delay       ; Delay
  704.     CBI     DDRB, 1         ; Set SCL high
  705.     RCALL   i2c_delay       ; Delay
  706.  
  707.     CLR     R16             ; Clear R16 which is the sign that data was received.
  708.  
  709.     RJMP    i2c_stop        ; We're done here.
  710.     RET                         ; Just in case? (i2c_stop has a RET in it)
  711.        
  712. ;***********************************************************************************************************************
  713. ; I2C start
  714. ;***********************************************************************************************************************
  715. ; Send I2C start condition
  716. ; Start condition is when the Data line (SDA) transistions from high to low while
  717. ; the clock (SCL) remains high, followed by the clock transistioning to low.
  718. ;
  719. i2c_start:
  720.     SBI     DDRB, 0             ; SDA Low
  721.     RCALL   i2c_delay           ; Delay
  722.     SBI     DDRB, 1             ; SCL Low
  723.     RCALL   i2c_delay           ; Delay
  724.     RET
  725.  
  726. ;***********************************************************************************************************************
  727. ; I2C stop
  728. ;***********************************************************************************************************************
  729. ; Send I2C stop condition
  730. ; Stop condition is when the Data line (SDA) transistions from high to low while
  731. ; the clock (SCL) remains high, followed by the clock transistioning to low.
  732. ;
  733. i2c_stop:
  734.     SBI     DDRB, 1         ; SCL Low (Just to make sure)
  735.     SBI     DDRB, 0         ; SDA Low (Just to make sure)
  736.     RCALL   i2c_delay
  737.     CBI     DDRB, 1         ; SCL High
  738.     RCALL   i2c_delay
  739.     CBI     DDRB, 0         ; SDA High
  740.     RCALL   i2c_delay       ; Post-data delay for padding.
  741.     RCALL   i2c_delay       ; Post-data delay for padding.
  742.     RET
  743.  
  744. ;***********************************************************************************************************************
  745. ; Delay routine
  746. ;***********************************************************************************************************************
  747. ; Registers R24 and R25 make a 16-bit word. Delay is set by loading a 16-bit value into this
  748. ; word and incrementing it until it overflows. The lower the initial value, the longer it will
  749. ; take to overflow and thus the longer the delay will be.
  750. ;
  751. ; This routine takes 7 + 4(65535 - n) cycles where n is the initial value of the registers
  752. ;
  753. ; At 1 MHz this puts the range of delay from 7 microseconds (n=65535) to 262,147 microseconds (0.262 sec)
  754. ;
  755. ; 0.001 seconds (1000 microseconds): n = 65,287 (0xFF6B)
  756. long_delay:
  757.     LDI     R24, 0x00           ; 0xFEFE happens to give 500.0Hz +/- 0.1% with the specific chip being tested with
  758.     LDI     R25, 0xFC           ; This is unburdened and actual code will probably affect the overall timing
  759.     usi_delay_loop:
  760.         ADIW    R24, 1
  761.         BRNE    usi_delay_loop
  762.  
  763. i2c_delay:
  764.     RET
  765. ;***********************************************************************************************************************
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement