twi_at91.c

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