altos/lpc: i2c appears to be working lpc-spi
authorKeith Packard <keithp@keithp.com>
Tue, 20 Dec 2022 02:27:17 +0000 (18:27 -0800)
committerKeith Packard <keithp@keithp.com>
Tue, 20 Dec 2022 02:34:01 +0000 (18:34 -0800)
The docs are full of lies; the example code here was useful:

https://github.com/tacowars/LPC1114-sandbox/blob/master/drivers/i2c/driver/i2c.c

Signed-off-by: Keith Packard <keithp@keithp.com>
src/Makefile.defs
src/lpc/ao_arch.h
src/lpc/ao_i2c_lpc.c
src/lpc/lpc.h
src/lpcxpresso/ao_demo.c
src/stm/ao_i2c_stm.c

index c2d5247cd1ea1cfcbf4129b2e7c291e2f87c3ed7..12e19dde32441cce23152b2fb8bb29fb30796073 100644 (file)
@@ -19,7 +19,7 @@ WARN_FLAGS=-Wall -Wextra -Werror -Wcast-align \
        -Warray-bounds=2 \
        -Wconversion
 
-OPT=-Os -Wl,-Map=$(PROGNAME)-$(VERSION).map
+OPT=-Os
 
 PICOLIBC_CFLAGS= \
        -specs=picolibc.specs \
@@ -29,7 +29,8 @@ PICOLIBC_CFLAGS= \
 AO_CFLAGS=\
        -std=gnu99 \
        -I. -I$(TOPDIR) -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers \
-       -I$(TOPDIR)/math -I$(TOPDIR)/draw -I$(TOPDIR)/product $(WARN_FLAGS) $(OPT) -g
+       -I$(TOPDIR)/math -I$(TOPDIR)/draw -I$(TOPDIR)/product \
+       $(WARN_FLAGS) $(OPT) -Wl,-Map=$(PROGNAME)-$(VERSION).map -g
 
 NICKLE=nickle
 ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex
index eedac777a46fae6a010a09ba04aaba6098c5b231..85e0a992646671ba7223fc3f843f99ac69d20ec2 100644 (file)
 #define ao_arch_block_interrupts()     asm("cpsid i")
 #define ao_arch_release_interrupts()   asm("cpsie i")
 
-/*
- * For now, we're running at a weird frequency
- */
-
-#if AO_HSE
-#define AO_PLLSRC      AO_HSE
-#else
-#define AO_PLLSRC      STM_HSI_FREQ
-#endif
-
-#define AO_PLLVCO      (AO_PLLSRC * AO_PLLMUL)
-#define AO_SYSCLK      (AO_PLLVCO / AO_PLLDIV)
-#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
-#define AO_PCLK1       (AO_HCLK / AO_APB1_PRESCALER)
-#define AO_PCLK2       (AO_HCLK / AO_APB2_PRESCALER)
-
-#if AO_APB1_PRESCALER == 1
-#define AO_TIM23467_CLK                AO_PCLK1
-#else
-#define AO_TIM23467_CLK                (2 * AO_PCLK1)
-#endif
-
-#if AO_APB2_PRESCALER == 1
-#define AO_TIM91011_CLK                AO_PCLK2
-#else
-#define AO_TIM91011_CLK                (2 * AO_PCLK2)
-#endif
-
 #define AO_LPC_NVIC_HIGH_PRIORITY      0
 #define AO_LPC_NVIC_CLOCK_PRIORITY     1
 #define AO_LPC_NVIC_MED_PRIORITY       2
index e8bde8add07f8fb74c89d544e4f380f8e267b935..ce759db0cc960e44ccad87f9f40dad5bea935246 100644 (file)
 
 #include <ao.h>
 
+#define AO_I2C_CLK     AO_LPC_SYSCLK
+
+//#define LPC_I2C_DEBUG
+
+/* Use 400kHz by default */
+#ifndef I2C_FAST
+#define I2C_FAST       1
+#endif
+
+#if I2C_FAST
+#define I2C_TIME       1250    /* ns per phase, 400kHz clock */
+#else
+#define I2C_TIME       5000    /* ns per phase, 100kHz clock */
+#endif
+
+#define I2C_DUTY       ((I2C_TIME * (AO_I2C_CLK / 1000)) / 1000000)
+
 static uint8_t ao_i2c_mutex;
 
+static uint8_t         i2c_addr;
 static const uint8_t   *i2c_send;
 static uint16_t                i2c_send_len;
 static uint8_t         *i2c_recv;
 static uint16_t                i2c_recv_len;
 static uint8_t         i2c_stop;
 static uint8_t         i2c_error;
+static uint8_t         i2c_done;
 
-static void
-_ao_i2c_put_byte(void)
-{
-       lpc_i2c.dat = *i2c_send++;
-       lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
-}
-
-static void
-_ao_i2c_get_byte(void)
-{
-       *i2c_recv++ = (uint8_t) lpc_i2c.dat;
-       i2c_recv_len--;
-       if (i2c_recv_len == 0) {
-               lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_AAC);
-               ao_wakeup(&i2c_recv_len);
-       } else {
-               lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
-       }
-}
-
+#ifdef LPC_I2C_DEBUG
 struct lpc_stat {
        const char      *where;
        uint8_t         stat;
+       uint8_t         i2c_con;
+       uint8_t         i2c_addr;
+       uint16_t        i2c_send_len;
+       uint16_t        i2c_recv_len;
 };
 
 #define NHISTORY       128
@@ -63,6 +67,10 @@ lpc_i2c_stat(const char *where)
        if (stat_count < NHISTORY) {
                stat_history[stat_count].where = where;
                stat_history[stat_count].stat = stat;
+               stat_history[stat_count].i2c_con = (uint8_t) lpc_i2c.conset;
+               stat_history[stat_count].i2c_addr = i2c_addr;
+               stat_history[stat_count].i2c_send_len = i2c_send_len;
+               stat_history[stat_count].i2c_recv_len = i2c_recv_len;
                stat_count++;
        }
        return stat;
@@ -74,63 +82,107 @@ lpc_i2c_dump(void)
        int i;
 
        for (i = 0; i < stat_count; i++) {
-               printf("0x%02x %s\n", stat_history[i].stat, stat_history[i].where);
+               printf("0x%02x c(%02x) a(%02x) s(%d) r(%d) %s\n",
+                      stat_history[i].stat,
+                      stat_history[i].i2c_con,
+                      stat_history[i].i2c_addr,
+                      stat_history[i].i2c_send_len,
+                      stat_history[i].i2c_recv_len,
+                      stat_history[i].where);
        }
        stat_count = 0;
 }
+#else
+#define lpc_i2c_stat(x) lpc_i2c.stat
+#define lpc_i2c_dump() do {} while(0)
+#endif
+
+static void
+lpc_i2c_error(void)
+{
+       lpc_i2c.conclr = ((1 << LPC_I2C_CONCLR_STAC) |
+                         (1 << LPC_I2C_CONCLR_SIC));
+       lpc_i2c.conset = (1 << LPC_I2C_CONSET_STO);
+       i2c_error = 1;
+       ao_wakeup(&i2c_done);
+}
+
+static void
+lpc_i2c_set_ack(void)
+{
+       /* if more than one byte to go, enable ack, else disable ack */
+       if (i2c_recv_len > 1)
+               lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
+       else
+               lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_AAC);
+}
 
 void
 lpc_i2c_isr(void)
 {
        switch (lpc_i2c_stat("isr")) {
        case LPC_I2C_STAT_ERROR:
-               lpc_i2c.conset = ((1 << LPC_I2C_CONSET_STO) |
-                                 (1 << LPC_I2C_CONSET_AA));
+               lpc_i2c_error();
                break;
        case LPC_I2C_STAT_START:
        case LPC_I2C_STAT_REPEAT_START:
-               i2c_error = 0;
-               /* fall through ... */
+               lpc_i2c.dat = i2c_addr;
+               lpc_i2c.conclr = ((1 << LPC_I2C_CONCLR_STAC) |
+                                 (1 << LPC_I2C_CONCLR_SIC));
+               break;
        case LPC_I2C_STAT_TX_START_ACK:
        case LPC_I2C_STAT_TX_ACK:
-               --i2c_send_len;
                if (i2c_send_len) {
-                       _ao_i2c_put_byte();
+                       lpc_i2c_stat("dout");
+                       lpc_i2c.dat = *i2c_send++;
+                       i2c_send_len--;
+                       lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
                } else {
-                       if (i2c_stop)
-                               lpc_i2c.conset =(1 << LPC_I2C_CONSET_STO);
-                       ao_wakeup(&i2c_send_len);
+                       lpc_i2c_stat("dend");
+                       if (i2c_stop) {
+                               lpc_i2c.conset = (1 << LPC_I2C_CONSET_STO);
+                               lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
+                       }
+                       i2c_done = 1;
+                       /*
+                        * Need to disable interrupts as the IRQ will
+                        * remain asserted until we clear it, and we
+                        * can't clear it if we're going to restart as
+                        * that will not generate a restart event
+                        */
+                       lpc_nvic_clear_enable(LPC_ISR_I2C_POS);
+                       ao_wakeup(&i2c_done);
                }
                break;
+       case LPC_I2C_STAT_RX_START_NACK:
        case LPC_I2C_STAT_TX_START_NACK:
        case LPC_I2C_STAT_TX_NACK:
-               lpc_i2c.conset = ((1 << LPC_I2C_CONSET_AA) |
-                                 (1 << LPC_I2C_CONSET_STO));
-               i2c_send_len = 0;
-               i2c_error = 1;
-               ao_wakeup(&i2c_send_len);
-               break;
        case LPC_I2C_STAT_TX_ARB_LOST:
-               lpc_i2c.conset =((1 << LPC_I2C_CONSET_AA)|
-                                (1 << LPC_I2C_CONSET_STA));
+               lpc_i2c_error();
                break;
        case LPC_I2C_STAT_RX_START_ACK:
-               lpc_i2c.conset = (1 << LPC_I2C_CONSET_AA);
+               lpc_i2c_set_ack();
+               lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
                break;
-       case LPC_I2C_STAT_RX_START_NACK:
        case LPC_I2C_STAT_RX_NACK:
-               lpc_i2c.conset = ((1 << LPC_I2C_CONSET_AA) |
-                                 (1 << LPC_I2C_CONSET_STO));
-               i2c_recv_len = 0;
-               i2c_error = 1;
-               ao_wakeup(&i2c_recv_len);
-               break;
+               /* fall through */
        case LPC_I2C_STAT_RX_ACK:
-               if (i2c_recv_len)
-                       _ao_i2c_get_byte();
+               if (i2c_recv_len) {
+                       *i2c_recv++ = (uint8_t) lpc_i2c.dat;
+                       i2c_recv_len--;
+                       lpc_i2c_set_ack();
+                       if (i2c_recv_len == 0) {
+                               if (i2c_stop) {
+                                       lpc_i2c.conset = (1 << LPC_I2C_CONSET_STO);
+                                       lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
+                               }
+                               i2c_done = 1;
+                               lpc_nvic_clear_enable(LPC_ISR_I2C_POS);
+                               ao_wakeup(&i2c_done);
+                       }
+               }
                break;
        }
-       lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
 }
 
 void
@@ -138,71 +190,90 @@ ao_i2c_get(uint8_t index)
 {
        (void) index;
        ao_mutex_get(&ao_i2c_mutex);
-       lpc_i2c.conset = (1 << LPC_I2C_CONSET_I2EN);
-       lpc_i2c.sclh = 0xff;
-       lpc_i2c.scll = 0xff;
+       i2c_error = 0;
 }
 
 void
 ao_i2c_put(uint8_t index)
 {
        (void) index;
-       lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_I2ENC);
        ao_mutex_put(&ao_i2c_mutex);
 }
 
 uint8_t
 ao_i2c_start(uint8_t index, uint16_t addr)
 {
-       uint8_t a = (uint8_t) addr;
-
        (void) index;
-       ao_arch_block_interrupts();
-       (void) lpc_i2c_stat("start");
-       i2c_send = &a;
-       i2c_send_len = 1;
-       i2c_stop = 0;
-       lpc_i2c.conset = (1 << LPC_I2C_CONSET_STA);
-       while (i2c_send_len)
-               ao_sleep(&i2c_send_len);
-       ao_arch_release_interrupts();
+       i2c_addr = (uint8_t) addr;
        return 0;
 }
 
 uint8_t
 ao_i2c_send(const void *block, uint16_t len, uint8_t index, uint8_t stop)
 {
+       uint8_t stopped = i2c_stop;
+
        (void) index;
        ao_arch_block_interrupts();
        (void) lpc_i2c_stat("send");
+
+       i2c_done = 0;
        i2c_send = block;
        i2c_send_len = len;
        i2c_stop = stop;
-       _ao_i2c_put_byte();
-       while (i2c_send_len) {
-               if (ao_sleep_for(&i2c_send_len, AO_SEC_TO_TICKS(2)))
-                       break;
-       }
+
+       /* Clear read bit */
+       i2c_addr &= 0xfe;
+
+       lpc_nvic_set_enable(LPC_ISR_I2C_POS);
+
+       /* Send start */
+       lpc_i2c.conset = (1 << LPC_I2C_CONSET_STA);
+
+       /* If we're restarting, clear the pending interrupt now */
+       if (!stopped)
+               lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
+
+       while (!i2c_done && !i2c_error)
+               ao_sleep(&i2c_done);
+
        ao_arch_release_interrupts();
-       lpc_i2c_dump();
-       return 0;
+       if (stop)
+               lpc_i2c_dump();
+       return i2c_error == 0;
 }
 
 uint8_t
 ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
 {
+       uint8_t stopped = i2c_stop;
        (void) index;
        ao_arch_block_interrupts();
+       (void) lpc_i2c_stat("recv");
+
+       i2c_done = 0;
        i2c_recv = block;
        i2c_recv_len = len;
        i2c_stop = stop;
-       /* Check to see if a byte is already here */
-       if (lpc_i2c.stat == LPC_I2C_STAT_RX_ACK)
-               _ao_i2c_get_byte();
-       while (i2c_recv_len)
-               ao_sleep(&i2c_recv_len);
+
+       /* Set read bit */
+       i2c_addr |= 0x01;
+
+       lpc_nvic_set_enable(LPC_ISR_I2C_POS);
+
+       /* Send start */
+       lpc_i2c.conset = (1 << LPC_I2C_CONSET_STA);
+
+       /* If we're restarting, clear the pending interrupt now */
+       if (!stopped)
+               lpc_i2c.conclr = (1 << LPC_I2C_CONCLR_SIC);
+
+       while (!i2c_done && !i2c_error)
+               ao_sleep(&i2c_done);
+
        ao_arch_release_interrupts();
-       return 0;
+       lpc_i2c_dump();
+       return i2c_error == 0;
 }
 
 void
@@ -219,7 +290,20 @@ ao_i2c_init(void)
        lpc_scb.presetctrl &= ~(1UL << LPC_SCB_PRESETCTRL_I2C_RST_N);
        lpc_scb.presetctrl |= (1 << LPC_SCB_PRESETCTRL_I2C_RST_N);
 
+       lpc_i2c.conclr = ((1 << LPC_I2C_CONCLR_I2ENC) |
+                         (1 << LPC_I2C_CONCLR_STAC) |
+                         (1 << LPC_I2C_CONCLR_STOC) |
+                         (1 << LPC_I2C_CONCLR_SIC) |
+                         (1 << LPC_I2C_CONCLR_AAC));
+
+       lpc_i2c.conset = (1 << LPC_I2C_CONSET_I2EN);
+
+       i2c_stop = 1;
+
+       /* experimentally determined to be off-by-two? */
+       lpc_i2c.sclh = I2C_DUTY - 2;
+       lpc_i2c.scll = I2C_DUTY - 2;
+
        /* Enable interrupts */
-       lpc_nvic_set_enable(LPC_ISR_I2C_POS);
        lpc_nvic_set_priority(LPC_ISR_I2C_POS, 0);
 }
index 218720e2f1e4f12070a3c41dbf49d725957bc276..ba715c6e6f56458c44f47a703cf21b58fd8f928e 100644 (file)
@@ -687,6 +687,7 @@ extern struct lpc_i2c lpc_i2c;
 
 #define LPC_I2C_CONCLR_AAC     2
 #define LPC_I2C_CONCLR_SIC     3
+#define LPC_I2C_CONCLR_STOC    4
 #define LPC_I2C_CONCLR_STAC    5
 #define LPC_I2C_CONCLR_I2ENC   6
 
index 4af184c6d129f86ca2f5c5b2c3e45c39d8313383..44ad9bc317dc8f964c51630b6ba1d04abf31db3e 100644 (file)
 static void
 ao_i2c_test(void)
 {
+       uint8_t reg[2] = { 0, 0 };
+       uint8_t val[1];
+       unsigned i = 0x0;
+       unsigned j;
+       uint8_t success;
+
+       i = ao_cmd_hex();
+       if (ao_cmd_status == ao_cmd_lex_error) {
+               i = 0;
+               ao_cmd_status = ao_cmd_success;
+       }
+       reg[0] = (uint8_t) (i >> 8);
+       reg[1] = (uint8_t) i;
        ao_i2c_get(0);
-       ao_i2c_start(0, 0x44);
-       ao_i2c_send("hello", 5, 0, 1);
+       ao_i2c_start(0, 0x29<<1);
+       success = ao_i2c_send(&reg, 2, 0, 0) && ao_i2c_recv(val, sizeof(val), 0, 1);
        ao_i2c_put(0);
+       if (!success) {
+               printf("i2c transaction failed\n");
+               return;
+       }
+       for (j = 0; j < sizeof(val); j++)
+               printf("reg 0x%04x = 0x%02x\n", i+j, val[j]);
 }
 
 const struct ao_cmds ao_test_cmds[] = {
index e634377cc996545a3118375f3705a81b40eb1abd..988e2a6c729bd90d0ee92f438c194ca8b2ebca56 100644 (file)
@@ -237,7 +237,7 @@ ao_i2c_wait_addr(uint8_t index)
 }
 
 uint8_t
-ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
+ao_i2c_send(const void *block, uint16_t len, uint8_t index, uint8_t stop)
 {
        struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
        uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
@@ -248,7 +248,7 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
        stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_DMAEN);
        ao_dma_set_transfer(tx_dma_index,
                            &stm_i2c->dr,
-                           block,
+                           (void *) block,
                            len,
                            (0 << STM_DMA_CCR_MEM2MEM) |
                            (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |