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