BeRTOS
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