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