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
|