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