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