Sistema l'errore da me commesso in fase di conversione...
[bertos.git] / drv / twi.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * This file is part of DevLib - See README.devlib for information.
6  * -->
7  *
8  * \brief Driver for the AVR ATMega TWI (implementation)
9  *
10  * \version $Id$
11  *
12  * \author Stefano Fedrigo <aleph@develer.com>
13  * \author Bernardo Innocenti <bernie@develer.com>
14  */
15
16 /*#*
17  *#* $Log$
18  *#* Revision 1.8  2007/06/07 14:35:12  batt
19  *#* Merge from project_ks.
20  *#*
21  *#* Revision 1.7  2006/07/19 12:56:26  bernie
22  *#* Convert to new Doxygen style.
23  *#*
24  *#* Revision 1.6  2006/03/20 17:49:50  bernie
25  *#* Make the TWI driver more generic to work with devices other than EEPROMS.
26  *#*
27  *#* Revision 1.5  2005/11/27 23:33:40  bernie
28  *#* Use appconfig.h instead of cfg/config.h.
29  *#*
30  *#* Revision 1.4  2005/04/11 19:10:28  bernie
31  *#* Include top-level headers from cfg/ subdir.
32  *#*
33  *#* Revision 1.3  2005/03/01 23:26:00  bernie
34  *#* Header fix.
35  *#*
36  *#* Revision 1.2  2005/01/25 08:36:56  bernie
37  *#* CONFIG_TWI_FREQ: New config param.
38  *#*
39  *#* Revision 1.1  2005/01/06 16:09:40  aleph
40  *#* Split twi/eeprom functions from eeprom module in separate twi module
41  *#*
42  *#*/
43
44 #include "twi.h"
45
46 #include <cfg/debug.h>
47 #include <cfg/cpu.h>
48 #include <cfg/macros.h> // BV()
49 #include <hw_cpu.h>  /* CLOCK_FREQ */
50 #include <appconfig.h>
51
52 #include <compat/twi.h>
53
54
55 /* Wait for TWINT flag set: bus is ready */
56 #define WAIT_TWI_READY  do {} while (!(TWCR & BV(TWINT)))
57
58 #define READ_BIT BV(0)
59
60
61 /**
62  * Send START condition on the bus.
63  *
64  * \return true on success, false otherwise.
65  */
66 static bool twi_start(void)
67 {
68         TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN);
69         WAIT_TWI_READY;
70
71         if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START)
72                 return true;
73
74         kprintf("!TW_(REP)START: %x\n", TWSR);
75         return false;
76 }
77
78
79 /**
80  * Send START condition and select slave for write.
81  * \c id is the device id comprehensive of address left shifted by 1.
82  * The LSB of \c id is ignored and reset to 0 for write operation.
83  *
84  * \return true on success, false otherwise.
85  */
86 bool twi_start_w(uint8_t id)
87 {
88         /*
89          * Loop on the select write sequence: when the eeprom is busy
90          * writing previously sent data it will reply to the SLA_W
91          * control byte with a NACK.  In this case, we must
92          * keep trying until the eeprom responds with an ACK.
93          */
94         while (twi_start())
95         {
96                 TWDR = id & ~READ_BIT;
97                 TWCR = BV(TWINT) | BV(TWEN);
98                 WAIT_TWI_READY;
99
100                 if (TW_STATUS == TW_MT_SLA_ACK)
101                         return true;
102                 else if (TW_STATUS != TW_MT_SLA_NACK)
103                 {
104                         kprintf("!TW_MT_SLA_(N)ACK: %x\n", TWSR);
105                         break;
106                 }
107         }
108
109         return false;
110 }
111
112
113 /**
114  * Send START condition and select slave for read.
115  * \c id is the device id comprehensive of address left shifted by 1.
116  * The LSB of \c id is ignored and set to 1 for read operation.
117  *
118  * \return true on success, false otherwise.
119  */
120 bool twi_start_r(uint8_t id)
121 {
122         if (twi_start())
123         {
124                 TWDR = id | READ_BIT;
125                 TWCR = BV(TWINT) | BV(TWEN);
126                 WAIT_TWI_READY;
127
128                 if (TW_STATUS == TW_MR_SLA_ACK)
129                         return true;
130
131                 kprintf("!TW_MR_SLA_ACK: %x\n", TWSR);
132         }
133
134         return false;
135 }
136
137
138 /**
139  * Send STOP condition.
140  */
141 void twi_stop(void)
142 {
143         TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO);
144 }
145
146
147 /**
148  * Put a single byte in master transmitter mode
149  * to the selected slave device through the TWI bus.
150  *
151  * \return true on success, false on error.
152  */
153 bool twi_put(const uint8_t data)
154 {
155         TWDR = data;
156         TWCR = BV(TWINT) | BV(TWEN);
157         WAIT_TWI_READY;
158         if (TW_STATUS != TW_MT_DATA_ACK)
159         {
160                 kprintf("!TW_MT_DATA_ACK: %x\n", TWSR);
161                 return false;
162         }
163         return true;
164 }
165
166
167 /**
168  * Send a sequence of bytes in master transmitter mode
169  * to the selected slave device through the TWI bus.
170  *
171  * \return true on success, false on error.
172  */
173 bool twi_send(const void *_buf, size_t count)
174 {
175         const uint8_t *buf = (const uint8_t *)_buf;
176
177         while (count--)
178         {
179                 if (!twi_put(*buf++))
180                         return false;
181         }
182         return true;
183 }
184
185
186 /**
187  * Receive a sequence of one or more bytes from the
188  * selected slave device in master receive mode through
189  * the TWI bus.
190  *
191  * Received data is placed in \c buf.
192  *
193  * \return true on success, false on error
194  */
195 bool twi_recv(void *_buf, size_t count)
196 {
197         uint8_t *buf = (uint8_t *)_buf;
198
199         /*
200          * When reading the last byte the TWEA bit is not
201          * set, and the eeprom should answer with NACK
202          */
203         while (count--)
204         {
205                 TWCR = BV(TWINT) | BV(TWEN) | (count ? BV(TWEA) : 0);
206                 WAIT_TWI_READY;
207
208                 if (count)
209                 {
210                         if (TW_STATUS != TW_MR_DATA_ACK)
211                         {
212                                 kprintf("!TW_MR_DATA_ACK: %x\n", TWSR);
213                                 return false;
214                         }
215                 }
216                 else
217                 {
218                         if (TW_STATUS != TW_MR_DATA_NACK)
219                         {
220                                 kprintf("!TW_MR_DATA_NACK: %x\n", TWSR);
221                                 return false;
222                         }
223                 }
224                 *buf++ = TWDR;
225         }
226
227         return true;
228 }
229
230
231 /**
232  * Initialize TWI module.
233  */
234 void twi_init(void)
235 {
236         ATOMIC(
237                 /*
238                  * This is pretty useless according to AVR's datasheet,
239                  * but it helps us driving the TWI data lines on boards
240                  * where the bus pull-up resistors are missing.  This is
241                  * probably due to some unwanted interaction between the
242                  * port pin and the TWI lines.
243                  */
244 #if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281
245                 PORTD |= BV(PD0) | BV(PD1);
246                 DDRD  |= BV(PD0) | BV(PD1);
247 #elif CPU_AVR_ATMEGA8
248                 PORTC |= BV(PC4) | BV(PC5);
249                 DDRC  |= BV(PC4) | BV(PC5);
250 #else
251                 #error Unsupported architecture
252 #endif
253
254                 /*
255                  * Set speed:
256                  * F = CLOCK_FREQ / (16 + 2*TWBR * 4^TWPS)
257                  */
258                 #ifndef CONFIG_TWI_FREQ
259                         #warning Using default value of 300000L for CONFIG_TWI_FREQ
260                         #define CONFIG_TWI_FREQ  300000L /* ~300 kHz */
261                 #endif
262                 #define TWI_PRESC 1       /* 4 ^ TWPS */
263
264                 TWBR = (CLOCK_FREQ / (2 * CONFIG_TWI_FREQ * TWI_PRESC)) - (8 / TWI_PRESC);
265                 TWSR = 0;
266                 TWCR = BV(TWEN);
267         );
268 }