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