-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathi2c.asm
340 lines (314 loc) · 7.59 KB
/
i2c.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
;;
; Copyright Jacques Deschênes 2019,2022
; This file is part of stm8_tbi
;
; stm8_tbi is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; stm8_tbi is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with stm8_tbi. If not, see <http://www.gnu.org/licenses/>.
;;
SEPARATE=0
.if SEPARATE
.module I2C
.include "config.inc"
.area CODE
.endif
;--------------------------------
; I2C peripheral drive
; pins:
; SCL -> PE1 -> CN8:10 | CN9:3
; SDA -> PE2 -> CN8:9 | CN9:5
;
; Support only 7 bit addressing
; and master mode
;--------------------------------
I2C_STATUS_DONE=7 ; bit 7 of i2c_status indicate operation completed
I2C_STATUS_NO_STOP=6 ; don't send a stop at end of transmission
;------------------------------
; i2c global interrupt handler
;------------------------------
I2cIntHandler:
ld a, I2C_SR2 ; errors status
clr I2C_SR2
and a,#15
jreq 1$
or a,i2c_status
_straz i2c_status
bset I2C_CR2,#I2C_CR2_STOP
iret
1$: ; no error detected
btjf i2c_status,#I2C_STATUS_DONE,2$
clr I2C_ITR
iret
; handle events
2$: clrw x
_ldaz i2c_idx
ld xl,a
btjt I2C_SR1,#I2C_SR1_SB,evt_sb
btjt I2C_SR1,#I2C_SR1_ADDR,evt_addr
btjt I2C_SR1,#I2C_SR1_BTF,evt_btf
btjt I2C_SR1,#I2C_SR1_TXE,evt_txe
btjt I2C_SR1,#I2C_SR1_RXNE,evt_rxne
btjt I2C_SR1,#I2C_SR1_STOPF,evt_stopf
iret
evt_sb: ; start bit, send address
_ldaz i2c_devid
ld I2C_DR,a
iret
evt_btf:
btjf I2C_SR3,#I2C_SR3_TRA,#evt_rxne
tnz i2c_count
jrne evt_txe_1
jra end_of_tx
evt_addr: ; EV6
btjt I2C_SR3,#I2C_SR3_TRA,evt_txe
iret
evt_txe:
tnz i2c_count
jreq end_of_tx
evt_txe_1:
ld a,([i2c_buf],x)
ld I2C_DR,a
_incz i2c_idx
dec i2c_count
1$: iret
; end of transmission
end_of_tx:
bset i2c_status,#I2C_STATUS_DONE
btjt i2c_status,#I2C_STATUS_NO_STOP,1$
bset I2C_CR2,#I2C_CR2_STOP
1$: clr I2C_ITR
iret
evt_rxne:
tnz i2c_count
jreq evt_stopf
1$: ld a,I2C_DR
ld ([i2c_buf],x),a
_incz i2c_idx
_decz i2c_count
jrne 4$
bres I2C_CR2,#I2C_CR2_ACK
4$: iret
evt_stopf:
ld a,I2C_DR
ld ([i2c_buf],x),a
bset I2C_CR2,#I2C_CR2_STOP
bset i2c_status,#I2C_STATUS_DONE
clr I2C_ITR
iret
; error message
I2C_ERR_NONE=0
I2C_ERR_NO_ACK=1 ; no ack received
I2C_ERR_OVR=2 ; overrun
I2C_ERR_ARLO=3 ; arbitration lost
I2C_ERR_BERR=4 ; bus error
i2c_error_msg:
.word i2c_error_none, i2c_error_no_ack,i2c_error_overrun
.word i2c_error_arbitration, i2c_error_bus_error
i2c_error: .asciz "i2c error, "
i2c_error_none: .asciz ""
i2c_error_no_ack: .asciz "no ACK received\n"
i2c_error_overrun: .asciz "overrrun\n"
i2c_error_arbitration: .asciz "bus arbitration lost\n"
i2c_error_bus_error: .asciz "misplaced start or stop\n"
i2c_error_timeout: .asciz "i2c operation time out,"
i2c_bytes_left: .asciz " bytes left.\n"
;---------------------------
; BASIC: I2C.ERROR
; display error message
;---------------------------
cmd_i2c_error:
_ldaz i2c_status
and a,#15
jrne 0$
; i2c report no error but there was a timeout.
bset I2C_CR2,#I2C_CR2_STOP
ldw x,#i2c_error_timeout
call puts
clrw x
_ldaz i2c_count
ld xl,a
call prt_i16
ldw x,#i2c_bytes_left
call puts
bres I2C_CR1,#I2C_CR1_PE
jra 6$
0$:
ldw x,#i2c_error
call puts
btjt i2c_status,#I2C_SR2_AF,1$
btjt i2c_status,#I2C_SR2_ARLO,2$
btjt i2c_status,#I2C_SR2_BERR,3$
btjf i2c_status,#I2C_SR2_OVR,4$
ret
1$:
ld a,#I2C_ERR_NO_ACK
jra 5$
2$:
ld a,#I2C_ERR_ARLO
jra 5$
3$:
ld a,#I2C_ERR_BERR
jra 5$
4$:
ld a,#I2C_ERR_OVR
5$: bres I2C_CR1,#I2C_CR1_PE
sll a
clrw x
ld xl,a
addw x,#i2c_error_msg
ldw x,(x)
call puts
_clrz i2c_status
6$:
; reset AFR to default peripheral
jp warm_start
;--------------------------------
; BASIC: I2C.open freq
; enable I2C peripheral
;
; freq:
; SCL in Khz
;--------------------------------
cmd_i2c_open:
; program i2c alternate function on PB4 and PB5
; get argument on xstack
call expect_integer
pushw y
; enable peripheral clock
bset CLK_PCKENR1,#CLK_PCKENR1_I2C
ld a,#FMSTR ; peripheral clock frequency
clr I2C_CR2
ld I2C_FREQR,a
; SCL fequency parameter
clr I2C_CCRH
pushw x ; A is ignored
ldw y,x
ldw x,#FMSTR*1000 ; Fmaster in Khz
divw x,y
; SLOW x=2*CCR
; FAST X=3*CCR | DUTY=1 -> 25*CCR
ldw y,x ;
; check for freq range
ld a,#2 ; slow mode divisor
popw x ; freq Khz
cpw x,#100
jrule 2$
; fast mode
bset I2C_CCRH,#I2C_CCRH_FAST
ld a,#3 ; fast mode divisor
cpw x,#320
jrne 2$
; fast mode DUTY=1
bset I2C_CCRH,#I2C_CCRH_DUTY
ld a,#25 ; FAST DUTY divisor
2$: ldw x,y
div x,a
ld a,xl
ld I2C_CCRL,a
; set risetime
ld a,#17
btjf I2C_CCRH,#I2C_CCRH_FAST,3$
; slow mode rise time max 1µS
ld a,#5
3$:
ld I2C_TRISER,a
; enable periphal
bset I2C_CR1,#I2C_CR1_PE
popw y
_next
;------------------------------------
; BASIC: I2C.CLOSE
; turn off i2c peripheral
;-------------------------------------
cmd_i2c_close:
; disable interrupts
bset I2C_CR2,#I2C_CR2_SWRST
nop
; disable peripheral
bres I2C_CR1,#I2C_CR1_PE
nop
; disable peripheral clock
bres CLK_PCKENR1,#CLK_PCKENR1_I2C
_next
;----------------------------
; set operation parameters
; called by i2c_write|i2c_read
;
; devid:
; 7 bit device identifier
;
; count:
; bytes to send|receive
;
; buf_addr:
; pointer to buffer
;
; no_stop:
; 0 set STOP bit at end
; 1 don't set STOP bit
;---------------------------
set_op_params:
clr i2c_status
call arg_list
cp a,#4
jreq 1$
jp syntax_error
1$: _i24_pop ; no_stop
jreq 2$
bset i2c_status,#I2C_STATUS_NO_STOP
2$: _i24_pop ; buf_addr
ldw i2c_buf,x
_i24_pop ; count
ld a,xl ; no more than 255
_straz i2c_count
_i24_pop ; devid
ld a,xl
_straz i2c_devid
ret
;--------------------------------
; BASIC: I2C.WRITE devid,count, buf_addr,no_stop
; write bytes to i2c device
; devid: device identifier
; count: of bytes to write
; buf_addr: address of bytes buffer
; no_stop: dont't send a stop
;---------------------------------
cmd_i2c_write:
call set_op_params
; same procedure for I2C.READ and I2C.WRITE
start_op:
_clrz i2c_idx
ldw x,#500
ldw timer,x ; expiration timer
ld a,#(1<<I2C_ITR_ITBUFEN)|(1<<I2C_ITR_ITERREN)|(1<<I2C_ITR_ITEVTEN)
ld I2C_ITR,a
ld a,#(1<<I2C_CR2_START)|(1<<I2C_CR2_ACK)
ld I2C_CR2,a
1$: btjt i2c_status,#I2C_STATUS_DONE,9$
_ldxz timer
jrne 1$
call cmd_i2c_error; operation timeout
9$:
_next
;-----------------------------------
; BASIC: I2C.READ devid,count, buf_addr, no_stop
; read bytes from device
; count: of bytes to read
; buf_addr: buffer address
; no_stop: don't send a stop condition
;-----------------------------------
func_i2c_read:
call set_op_params
_ldaz i2c_devid
or a,#1 ; bit0 -> 1 for read
_straz i2c_devid
jra start_op