i2c_slave.asm


1
; By: Clemens Helfmeier, ClemensHelfmeier AT gmx DOT de
2
;
3
;
4
; i2c_slave implementation
5
; needs int0
6
; needs 2 high and 1 low register and one pointer (z)
7
8
.equ  i2c_pin    = pinB
9
.equ  i2c_port  = portB
10
.equ  i2c_dir    = ddrB
11
12
.equ  i2c_bitSCL  = 0
13
.equ  i2c_bitSDA  = 1
14
15
.equ  i2c_valSCL  = 1<<i2c_bitSCL
16
.equ  i2c_valSDA  = 1<<i2c_bitSDA
17
18
;.def  i2c_data  = r16
19
;.def  i2c_state  = r17
20
;.def  i2c_SREG  = r8
21
22
.equ  i2c_slaveAddr  = 0x52
23
24
;macros
25
.macro i2c_waitSCL_low
26
i2c_waitSCL_low0:  sbic i2c_pin, i2c_bitSCL
27
    rjmp i2c_waitSCL_low0
28
.endm
29
30
.macro i2c_waitSCL_high
31
i2c_waitSCL_high0:  sbis i2c_pin, i2c_bitSCL
32
    rjmp i2c_waitSCL_high0
33
.endm
34
35
.macro i2c_readAddress
36
  ; reads the address into @0
37
  ; this is shorter than readByte, because we do not need to detect a stop condition
38
  ldi @0, 0x01
39
i2c_readAddress0:
40
  i2c_waitSCL_high
41
  clc
42
  sbic i2c_pin, i2c_bitSDA
43
    sec
44
  rol @0
45
  i2c_waitSCL_low
46
  brcc i2c_readAddress0
47
.endm
48
49
.macro i2c_readByte  ; returns when scl is low, after last bit
50
  ; reads one byte into @0
51
  ; read the first bit (here could also come a stop condition or a rep-start)
52
  i2c_waitSCL_high
53
  in @0, i2c_pin
54
  andi @0, i2c_valSDA | i2c_valSCL
55
i2c_readByte2:
56
  in i2c_state, i2c_pin
57
  andi i2c_state, i2c_valSDA | i2c_valSCL
58
  cp i2c_state, @0
59
  breq i2c_readByte2  ; nothing happend
60
  
61
  andi i2c_state, i2c_valSCL
62
  breq i2c_readByte3  ; first bit recieved
63
  
64
  andi @0, i2c_valSDA
65
  brne i2c_repStart  ; rep-start-condition
66
  i2c_return    ; stop-condition
67
68
i2c_readByte3:
69
  andi @0, i2c_valSDA
70
  breq i2c_readByte4
71
    ldi @0, 0x03
72
i2c_readByte4:
73
  ori @0, 0x02    ; end-mark for finish
74
i2c_readByte0:
75
  i2c_waitSCL_high
76
  clc
77
  sbic i2c_pin, i2c_bitSDA
78
i2c_readByte1:
79
    sec
80
  rol @0
81
  i2c_waitSCL_low
82
  brcc i2c_readByte0
83
.endm
84
85
.macro i2c_writeByte  ;returns when scl is low, after last bit
86
  sec
87
i2c_writeByte0:
88
  rol @0
89
  breq i2c_writeByte3
90
  brcc i2c_writeByte1
91
    cbi i2c_dir, i2c_bitSDA
92
    rjmp i2c_writeByte2
93
i2c_writeByte1:
94
    sbi i2c_dir, i2c_bitSDA
95
    ;rjmp i2c_writeByte2
96
i2c_writeByte2:  ;sda = bit now
97
  i2c_waitSCL_high  ; wait one clock pulse
98
  i2c_waitSCL_low
99
  clc
100
  rjmp i2c_writeByte0
101
i2c_writeByte3:  ; transmittion finished
102
  cbi i2c_dir, i2c_bitSDA
103
.endm
104
105
.macro i2c_getAck  ; returns when scl is low, after ack
106
  i2c_waitSCL_high
107
  andi @0, 0xFE
108
  sbis i2c_pin, i2c_bitSDA
109
    ori @0, 0x01
110
  i2c_waitSCL_low
111
.endm
112
113
.macro i2c_putAck  ; returns if scl is low, after ack
114
  sbi i2c_dir, i2c_bitSDA
115
  i2c_waitSCL_high
116
  i2c_waitSCL_low
117
  cbi i2c_dir, i2c_bitSDA
118
.endm
119
120
.macro i2c_handleInterrupts  ; this must be called between a putWaitState and remWaitState
121
  ; needs a high register for temporary usage
122
  ; remove interrupt-bit from GIFR
123
  ldi @0, 0x40
124
  out GIFR, @0
125
  sei  ; switch interrupts to on
126
  nop  ; do nothing
127
  nop  ; max. number of 2 interrupts
128
  cli  ; switch interrupts to off
129
.endm
130
.macro i2c_putWaitState
131
  sbi i2c_dir, i2c_bitSCL
132
.endm
133
.macro i2c_remWaitState
134
  cbi i2c_dir, i2c_bitSCL
135
.endm
136
137
.macro i2c_putByte  ; store a recieved byte, could be a call
138
  ; this must be the address -> z-register
139
  mov zl, @0
140
  clr zh
141
.endm
142
143
.macro i2c_getByte  ; get a byte for transmittion, could be a call
144
  cpi zl, 10
145
  brlt i2c_getByte0
146
    clr zl
147
    clr zh
148
i2c_getByte0:
149
  ld @0, z+
150
.endm
151
152
.macro i2c_addressed  ; this device has been addressed, could be a call
153
      ; and take more than only a few clock cycles
154
.endm
155
156
.macro i2c_return  ; return from interrupt, restore SREG, restore Interrupts
157
  ldi i2c_data, 0x40
158
  out GIFR, i2c_data  ; clear interrupt flag
159
  out SREG, i2c_SREG
160
  reti
161
.endm
162
  
163
164
i2c_int_data:
165
  ; only proceed on when start condition happened:
166
  sbis i2c_pin, i2c_bitSCL
167
    reti      ; the scl line was low -> normal change of sda line
168
  
169
  in i2c_SREG, SREG    ; the scl line was high -> start condition detected
170
171
i2c_repStart:
172
  i2c_waitSCL_low
173
  
174
  ; handle the i2c_start_condition
175
  i2c_readAddress i2c_data    ; read the address byte
176
  
177
  ; decode the address
178
  mov i2c_state, i2c_data
179
  andi i2c_state, 0x01
180
  andi i2c_data, 0xFE
181
  cpi i2c_data, i2c_slaveAddr
182
  breq i2c_addressMatch    ; if address matches, handle this
183
    
184
    i2c_return  ; else return and wait for next start condition
185
186
i2c_addressMatch:
187
  ; this device has been addressed -> give ack
188
  i2c_putAck
189
  i2c_putWaitState
190
  i2c_addressed
191
  rjmp i2c_readNextByte1
192
  
193
;i2c_write:  ; master write mode
194
;  i2c_putAck
195
i2c_readNextByte:
196
  i2c_putWaitState
197
i2c_readNextByte1:
198
  i2c_handleInterrupts i2c_data  ; handle some interrupts if needed
199
  
200
  sbrc i2c_state, 0    ; if master-read-mode
201
    rjmp i2c_read    ; read the thing
202
203
  i2c_remWaitState    ; remove wait-state
204
          ; else master-write
205
  i2c_readByte i2c_data    ; read one byte. this must also detect a stop-condition
206
207
  i2c_putAck      ; give the ack
208
  i2c_putWaitState    ; put the wait state
209
  
210
  i2c_putByte i2c_data    ; store the recieved byte internally
211
  
212
  i2c_remWaitState
213
  
214
  rjmp i2c_readNextByte1    ; and wait for next byte
215
216
i2c_read:  ; master read mode
217
  i2c_getByte i2c_data    ; get the next byte to be read
218
  
219
  i2c_remWaitState
220
  i2c_writeByte i2c_data
221
  
222
  i2c_getAck i2c_state
223
224
  sbrc i2c_state, 0
225
    rjmp i2c_readNextByte  ; read the next byte
226
  
227
  ; no more bytes to read, the master didn't acknowled
228
  i2c_return
229
  
230
i2c_init:
231
  ; initialize the peripheral
232
  cbi i2c_port, i2c_bitSDA
233
  cbi i2c_port, i2c_bitSCL
234
  cbi i2c_dir, i2c_bitSDA
235
  cbi i2c_dir, i2c_bitSCL
236
  ;initialize interrupt
237
  ldi i2c_data, 0x40
238
  out GIMSK, i2c_data
239
  in i2c_data, MCUCR
240
  andi i2c_data, 0xFC
241
  ;falling edge of int0
242
  ori i2c_data, 0x02
243
  out MCUCR, i2c_data
244
  ret