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