twi_at91.c
Go to the documentation of this file.
00001
00039 #include "twi_at91.h"
00040
00041 #include "cfg/cfg_i2c.h"
00042 #include <cfg/compiler.h>
00043 #include <cfg/debug.h>
00044 #include <cfg/macros.h>
00045 #include <cfg/module.h>
00046
00047 #include <drv/timer.h>
00048
00049 #include <io/arm.h>
00050
00054 #define TWI_TIMEOUT ms_to_ticks(50)
00055 
00066 bool twi_write(uint8_t id, twi_iaddr_t byte1, twi_iaddr_t byte2, twi_iaddr_t byte3, const void *_buf, size_t size)
00067 {
00068     uint8_t addr_size = 0;
00069     const uint8_t *buf = (const uint8_t *)_buf;
00070     ticks_t start;
00071
00072     /* At least 1 byte *must* be transmitted, thanks to crappy hw implementation */
00073     ASSERT(size >= 1);
00074
00075     /* Check internal byte address presence */
00076     if (byte1 != TWI_NO_IADDR)
00077         addr_size++;
00078
00079     if (byte2 != TWI_NO_IADDR)
00080     {
00081         ASSERT(addr_size == 1);
00082         addr_size++;
00083     }
00084
00085     if (byte3 != TWI_NO_IADDR)
00086     {
00087         ASSERT(addr_size == 2);
00088         addr_size++;
00089     }
00090
00091     start = timer_clock();
00092     /* Wait tx buffer empty */
00093     while (!(TWI_SR & BV(TWI_TXRDY)))
00094     {
00095         if (timer_clock() - start > TWI_TIMEOUT)
00096             return false;
00097     }
00098
00099     /* Set slave address and (optional) internal slave addresses */
00100     TWI_MMR = (uint32_t)id << TWI_DADR_SHIFT | (uint32_t)addr_size << TWI_IADRSZ_SHIFT;
00101
00102     TWI_IADR = ((uint32_t)(byte3 & 0xff) << 16) | ((uint32_t)(byte2 & 0xff) << 8) | ((uint32_t)(byte1 & 0xff));
00103
00104     while (size--)
00105     {
00106         /* Send data */
00107         TWI_THR = *buf++;
00108
00109         start = timer_clock();
00110         /* Wait tx buffer empty */
00111         while (!(TWI_SR & BV(TWI_TXRDY)))
00112         {
00113             if (timer_clock() - start > TWI_TIMEOUT)
00114                 return false;
00115         }
00116     }
00117
00118     /* Wait transmit complete bit */
00119     start = timer_clock();
00120     while (!(TWI_SR & BV(TWI_TXCOMP)))
00121     {
00122         if (timer_clock() - start > TWI_TIMEOUT)
00123             return false;
00124     }
00125
00126     return true;
00127 }
00128
00129
00140 bool twi_read(uint8_t id, twi_iaddr_t byte1, twi_iaddr_t byte2, twi_iaddr_t byte3, void *_buf, size_t size)
00141 {
00142     uint8_t addr_size = 0;
00143     uint8_t *buf = (uint8_t *)_buf;
00144     bool stopped = false;
00145     ticks_t start;
00146
00147     /* At least 1 byte *must* be transmitted, thanks to crappy twi implementation */
00148     ASSERT(size >= 1);
00149
00150     /* Check internal byte address presence */
00151     if (byte1 != TWI_NO_IADDR)
00152         addr_size++;
00153
00154     if (byte2 != TWI_NO_IADDR)
00155     {
00156         ASSERT(addr_size == 1);
00157         addr_size++;
00158     }
00159
00160     if (byte3 != TWI_NO_IADDR)
00161     {
00162         ASSERT(addr_size == 2);
00163         addr_size++;
00164     }
00165
00166     /* Wait tx buffer empty */
00167     start = timer_clock();
00168     while (!(TWI_SR & BV(TWI_TXRDY)))
00169     {
00170         if (timer_clock() - start > TWI_TIMEOUT)
00171             return false;
00172     }
00173
00174
00175     /* Set slave address and (optional) internal slave addresses */
00176     TWI_MMR = ((uint32_t)id << TWI_DADR_SHIFT) | BV(TWI_MREAD) | ((uint32_t)addr_size << TWI_IADRSZ_SHIFT);
00177
00178     TWI_IADR = ((uint32_t)(byte3 & 0xff) << 16) | ((uint32_t)(byte2 & 0xff) << 8) | ((uint32_t)(byte1 & 0xff));
00179
00180     /*
00181      * Start reception.
00182      * Kludge: if we want to receive only 1 byte, the stop but *must* be set here
00183      * (thanks to crappy twi implementation again).
00184      */
00185     if (size == 1)
00186     {
00187         TWI_CR = BV(TWI_START) | BV(TWI_STOP);
00188         stopped = true;
00189     }
00190     else
00191         TWI_CR = BV(TWI_START);
00192
00193     while (size--)
00194     {
00195         /* If we are at the last byte, inform the crappy hw that we
00196            want to stop the reception. */
00197         if (!size && !stopped)
00198             TWI_CR = BV(TWI_STOP);
00199
00200         /* Wait until a byte is received */
00201         start = timer_clock();
00202         while (!(TWI_SR & BV(TWI_RXRDY)))
00203         {
00204             if (timer_clock() - start > TWI_TIMEOUT)
00205             {
00206                 TWI_CR = BV(TWI_STOP);
00207                 return false;
00208             }
00209         }
00210
00211
00212         *buf++ = TWI_RHR;
00213     }
00214
00215     /* Wait transmit complete bit */
00216     start = timer_clock();
00217     while (!(TWI_SR & BV(TWI_TXCOMP)))
00218     {
00219         if (timer_clock() - start > TWI_TIMEOUT)
00220             return false;
00221     }
00222
00223     return true;
00224 }
00225
00226 MOD_DEFINE(twi);
00227
00231 void twi_init(void)
00232 {
00233     /* Disable PIO on TWI pins */
00234     PIOA_PDR = BV(TWD) | BV(TWCK);
00235
00236     /* Enable oper drain on TWI pins */
00237     PIOA_MDER = BV(TWD);
00238
00239     /* Disable all irqs */
00240     TWI_IDR = 0xFFFFFFFF;
00241
00242     TWI_CR = BV(TWI_SWRST);
00243
00244     /* Enable master mode */
00245     TWI_CR = BV(TWI_MSEN);
00246
00247     PMC_PCER = BV(TWI_ID);
00248
00249     /*
00250      * Compute twi clock.
00251      * CLDIV = ((Tlow * 2^CKDIV) -3) * Tmck
00252      * CHDIV = ((THigh * 2^CKDIV) -3) * Tmck
00253      * Only CLDIV is computed since CLDIV = CHDIV (50% duty cycle)
00254      */
00255     uint16_t cldiv, ckdiv = 0;
00256     while ((cldiv = ((CPU_FREQ / (2 * CONFIG_I2C_FREQ)) - 3) / (1 << ckdiv)) > 255)
00257         ckdiv++;
00258
00259     /* Atmel errata states that ckdiv *must* be less than 5 for unknown reason */
00260     ASSERT(ckdiv < 5);
00261
00262     TWI_CWGR = ((uint32_t)ckdiv << TWI_CKDIV_SHIFT) | (cldiv << TWI_CLDIV_SHIFT) | (cldiv << TWI_CHDIV_SHIFT);
00263     TRACEMSG("TWI_CWGR [%08lx]", TWI_CWGR);
00264
00265     MOD_INIT(twi);
00266 }
00267