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