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