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