add i2c support
authorOlivier Matz <zer0@droids-corp.org>
Thu, 17 Jul 2014 17:56:20 +0000 (19:56 +0200)
committerOlivier Matz <zer0@droids-corp.org>
Thu, 17 Jul 2014 17:56:20 +0000 (19:56 +0200)
Makefile
commands.c
i2c_protocol.c [new file with mode: 0644]
i2c_protocol.h [new file with mode: 0644]
main.c
main.h

index 3ff895f..7fcda32 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ SRC += parse_monitor.c
 SRC += cmdline.c
 SRC += beep.c
 SRC += eeprom_config.c
 SRC += cmdline.c
 SRC += beep.c
 SRC += eeprom_config.c
+SRC += i2c_protocol.c
 
 CFLAGS += -W -Wall -Werror
 
 
 CFLAGS += -W -Wall -Werror
 
index c6a6cc9..befd1c9 100644 (file)
@@ -48,6 +48,7 @@
 #include "main.h"
 #include "cmdline.h"
 #include "beep.h"
 #include "main.h"
 #include "cmdline.h"
 #include "beep.h"
+#include "i2c_protocol.h"
 #include "eeprom_config.h"
 
 /* commands_gen.c */
 #include "eeprom_config.h"
 
 /* commands_gen.c */
@@ -2244,6 +2245,38 @@ const parse_inst_t PROGMEM cmd_eeprom_list = {
 };
 
 
 };
 
 
+/* ************* */
+
+struct cmd_test_result {
+       fixed_string_t cmd;
+};
+
+extern uint8_t imuboard; /* XXX test */
+static void cmd_test_parsed(void *parsed_result, void *data)
+{
+       (void)parsed_result;
+       (void)data;
+       printf("%d\n", imuboard);
+       i2c_protocol_debug();
+}
+
+const char PROGMEM str_test[] = "test";
+const parse_token_string_t PROGMEM cmd_test_cmd =
+       TOKEN_STRING_INITIALIZER(struct cmd_test_result, cmd,
+                                str_test);
+
+const char PROGMEM help_test[] = "test";
+const parse_inst_t PROGMEM cmd_test = {
+       .f = cmd_test_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = help_test,
+       .tokens = {        /* token list, NULL terminated */
+               (PGM_P)&cmd_test_cmd,
+               NULL,
+       },
+};
+
+
 /* ************* */
 
 /* in progmem */
 /* ************* */
 
 /* in progmem */
@@ -2301,5 +2334,6 @@ const parse_ctx_t PROGMEM main_ctx[] = {
        &cmd_eeprom_add,
        &cmd_eeprom_add2,
        &cmd_eeprom_list,
        &cmd_eeprom_add,
        &cmd_eeprom_add2,
        &cmd_eeprom_list,
+       &cmd_test,
        NULL,
 };
        NULL,
 };
diff --git a/i2c_protocol.c b/i2c_protocol.c
new file mode 100644 (file)
index 0000000..6734453
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ *  Copyright Droids Corporation (2009)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Revision : $Id: i2c_protocol.c,v 1.8 2009-11-08 17:24:33 zer0 Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <aversive/pgmspace.h>
+#include <aversive/wait.h>
+#include <aversive/error.h>
+
+#include <callout.h>
+#include <i2c.h>
+
+#include "../fpv-common/i2c_commands.h"
+#include "main.h"
+#include "i2c_protocol.h"
+
+#define I2C_STATE_MAX 2
+#define I2C_PERIOD_MS 50
+
+#define I2C_TIMEOUT 100 /* ms */
+#define I2C_MAX_ERRORS 40
+
+uint8_t imuboard = 0; /* XXX test */
+
+static volatile uint8_t i2c_poll_num = 0;
+static volatile uint8_t i2c_state = 0;
+static volatile uint16_t i2c_errors = 0;
+
+#define OP_READY 0 /* no i2c op running */
+#define OP_POLL  1 /* a user command is running */
+#define OP_CMD   2 /* a polling (req / ans) is running */
+
+static volatile uint8_t running_op = OP_READY;
+
+#define I2C_MAX_LOG 3
+static uint8_t error_log = 0;
+
+static struct callout i2c_timer;
+
+static int8_t i2c_req_imuboard_status(void);
+
+/* used for commands */
+uint8_t command_buf[I2C_SEND_BUFFER_SIZE];
+volatile int8_t command_dest=-1;
+volatile uint8_t command_size=0;
+
+#define I2C_ERROR(args...) do {                                                \
+               if (error_log < I2C_MAX_LOG) {                          \
+                       ERROR(E_USER_I2C_PROTO, args);                  \
+                       error_log ++;                                   \
+                       if (error_log == I2C_MAX_LOG) {                 \
+                               ERROR(E_USER_I2C_PROTO,                 \
+                                     "i2c logs are now warnings");     \
+                       }                                               \
+               }                                                       \
+               else                                                    \
+                       WARNING(E_USER_I2C_PROTO, args);                \
+       } while(0)
+
+void i2c_protocol_debug(void)
+{
+       printf_P(PSTR("I2C protocol debug infos:\r\n"));
+       printf_P(PSTR("  i2c_state=%d\r\n"), i2c_state);
+       printf_P(PSTR("  i2c_errors=%d\r\n"), i2c_errors);
+       printf_P(PSTR("  running_op=%d\r\n"), running_op);
+       printf_P(PSTR("  command_size=%d\r\n"), command_size);
+       printf_P(PSTR("  command_dest=%d\r\n"), command_dest);
+       printf_P(PSTR("  i2c_status=%x\r\n"), i2c_status());
+}
+
+static void i2cproto_next_state(uint8_t inc)
+{
+       i2c_state += inc;
+       if (i2c_state >= I2C_STATE_MAX) {
+               i2c_state = 0;
+               i2c_poll_num ++;
+       }
+}
+
+void i2cproto_wait_update(void)
+{
+       uint8_t poll_num;
+       poll_num = i2c_poll_num;
+       (void)poll_num;
+       //WAIT_COND_OR_TIMEOUT((i2c_poll_num-poll_num) > 1, 150); /* XXX todo */
+}
+
+/* called periodically : the goal of this 'thread' is to send requests
+ * and read answers on i2c slaves in the correct order. */
+static void i2c_poll_slaves(struct callout_mgr *cm, struct callout *tim, void *arg)
+{
+       uint8_t flags;
+       int8_t err;
+
+       (void)cm;
+       (void)tim;
+       (void)arg;
+
+#if 0
+       static uint8_t a = 0;
+
+       a++;
+       if (a & 0x4)
+               LED2_TOGGLE();
+#endif
+
+       /* already running */
+       IRQ_LOCK(flags);
+       if (running_op != OP_READY) {
+               IRQ_UNLOCK(flags);
+               goto reschedule;
+       }
+
+       /* if a command is ready to be sent, so send it */
+       if (command_size) {
+               running_op = OP_CMD;
+               err = i2c_send(command_dest, command_buf, command_size,
+                            I2C_CTRL_GENERIC);
+               if (err < 0)
+                       goto error;
+               IRQ_UNLOCK(flags);
+               goto reschedule;
+       }
+
+       /* no command, so do the polling */
+       running_op = OP_POLL;
+
+       switch(i2c_state) {
+
+       /* poll status of imuboard */
+#define I2C_REQ_IMUBOARD 0
+       case I2C_REQ_IMUBOARD:
+               if ((err = i2c_req_imuboard_status()))
+                       goto error;
+               break;
+
+#define I2C_ANS_IMUBOARD 1
+       case I2C_ANS_IMUBOARD:
+               if ((err = i2c_recv(I2C_IMUBOARD_ADDR,
+                                   sizeof(struct i2c_ans_imuboard_status),
+                                   I2C_CTRL_GENERIC)))
+                       goto error;
+               break;
+
+       /* sync with I2C_STATE_MAX */
+
+       /* nothing, go to the first request */
+       default:
+               i2c_state = 0;
+               running_op = OP_READY;
+       }
+       IRQ_UNLOCK(flags);
+
+       goto reschedule;
+
+ error:
+       running_op = OP_READY;
+       IRQ_UNLOCK(flags);
+       i2c_errors++;
+       if (i2c_errors > I2C_MAX_ERRORS) {
+               I2C_ERROR("I2C send is_cmd=%d proto_state=%d "
+                     "err=%d i2c_status=%x", !!command_size, i2c_state, err, i2c_status());
+               i2c_reset();
+               i2c_errors = 0;
+       }
+
+ reschedule:
+       /* reschedule */
+       callout_reschedule(cm, tim, I2C_PERIOD_MS);
+}
+
+/* called when the xmit is finished */
+void i2c_sendevent(int8_t size)
+{
+       if (size > 0) {
+               if (running_op == OP_POLL) {
+                       i2cproto_next_state(1);
+               }
+               else
+                       command_size = 0;
+       }
+       else {
+               i2c_errors++;
+               NOTICE(E_USER_I2C_PROTO, "send error state=%d size=%d "
+                       "op=%d", i2c_state, size, running_op);
+               if (i2c_errors > I2C_MAX_ERRORS) {
+                       I2C_ERROR("I2C error, slave not ready");
+                       i2c_reset();
+                       i2c_errors = 0;
+               }
+
+               if (running_op == OP_POLL) {
+                       /* skip associated answer */
+                       i2cproto_next_state(2);
+               }
+       }
+       running_op = OP_READY;
+}
+
+/* called rx event */
+void i2c_recvevent(uint8_t * buf, int8_t size)
+{
+       if (running_op == OP_POLL)
+               i2cproto_next_state(1);
+
+       /* recv is only trigged after a poll */
+       running_op = OP_READY;
+
+       if (size < 0) {
+               goto error;
+       }
+
+       switch (buf[0]) {
+
+       case I2C_ANS_IMUBOARD_STATUS: {
+               struct i2c_ans_imuboard_status * ans =
+                       (struct i2c_ans_imuboard_status *)buf;
+
+               if (size != sizeof (*ans))
+                       goto error;
+
+               /* status */
+               imuboard = ans->test;
+
+               break;
+       }
+
+       default:
+               break;
+       }
+
+       return;
+ error:
+       i2c_errors++;
+       NOTICE(E_USER_I2C_PROTO, "recv error state=%d op=%d",
+              i2c_state, running_op);
+       if (i2c_errors > I2C_MAX_ERRORS) {
+               I2C_ERROR("I2C error, slave not ready");
+               i2c_reset();
+               i2c_errors = 0;
+       }
+}
+
+void i2c_recvbyteevent(uint8_t hwstatus, uint8_t i, uint8_t c)
+{
+       (void)hwstatus;
+       (void)i;
+       (void)c;
+}
+
+/* ******** ******** ******** ******** */
+/* commands */
+/* ******** ******** ******** ******** */
+
+
+static int8_t
+i2c_send_command(uint8_t addr, uint8_t * buf, uint8_t size)
+{
+       uint8_t flags;
+       uint16_t ms = get_time_ms();
+
+       while ((get_time_ms() - ms) < I2C_TIMEOUT) {
+               IRQ_LOCK(flags);
+               if (command_size == 0) {
+                       memcpy(command_buf, buf, size);
+                       command_size = size;
+                       command_dest = addr;
+                       IRQ_UNLOCK(flags);
+                       return 0;
+               }
+               IRQ_UNLOCK(flags);
+       }
+       /* this should not happen... except if we are called from an
+        * interrupt context, but it's forbidden */
+       I2C_ERROR("I2C command send failed");
+       return -EBUSY;
+}
+
+static int8_t i2c_req_imuboard_status(void)
+{
+       struct i2c_req_imuboard_status buf;
+       int8_t err;
+
+       buf.hdr.cmd = I2C_REQ_IMUBOARD_STATUS;
+       err = i2c_send(I2C_IMUBOARD_ADDR, (uint8_t*)&buf,
+                       sizeof(buf), I2C_CTRL_GENERIC);
+
+       return err;
+}
+
+int8_t i2c_led_control(uint8_t addr, uint8_t led, uint8_t state)
+{
+       struct i2c_cmd_led_control buf;
+       buf.hdr.cmd = I2C_CMD_GENERIC_LED_CONTROL;
+       buf.led_num = led;
+       buf.state = state;
+       return i2c_send_command(addr, (uint8_t*)&buf, sizeof(buf));
+}
+
+void i2c_protocol_init(void)
+{
+       callout_init(&i2c_timer, i2c_poll_slaves, NULL, I2C_PRIO);
+       callout_schedule(&xbeeboard.intr_cm, &i2c_timer, I2C_PERIOD_MS);
+}
diff --git a/i2c_protocol.h b/i2c_protocol.h
new file mode 100644 (file)
index 0000000..d101279
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  Copyright Droids Corporation (2009)
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Revision : $Id: i2c_protocol.h,v 1.6 2009-11-08 17:24:33 zer0 Exp $
+ *
+ */
+
+#ifndef _I2C_PROTOCOL_H_
+#define _I2C_PROTOCOL_H_
+
+void i2c_protocol_init(void);
+void i2c_protocol_debug(void);
+
+void i2cproto_wait_update(void);
+
+
+void i2c_recvevent(uint8_t *buf, int8_t size);
+void i2c_recvbyteevent(uint8_t hwstatus, uint8_t i, uint8_t c);
+void i2c_sendevent(int8_t size);
+
+int8_t i2c_led_control(uint8_t addr, uint8_t led, uint8_t state);
+
+
+#endif
diff --git a/main.c b/main.c
index 4529d6e..aded312 100644 (file)
--- a/main.c
+++ b/main.c
@@ -27,6 +27,7 @@
 
 /* fuses:
  * avrdude -p atmega1284p -P usb -c avrispmkii -U lfuse:w:0xff:m -U hfuse:w:0x91:m -U efuse:w:0xff:m
 
 /* fuses:
  * avrdude -p atmega1284p -P usb -c avrispmkii -U lfuse:w:0xff:m -U hfuse:w:0x91:m -U efuse:w:0xff:m
+ * -> it failed but I answered y, then make reset and it was ok
  */
 
 #include <aversive.h>
  */
 
 #include <aversive.h>
 #include <parse.h>
 #include <rdline.h>
 #include <timer.h>
 #include <parse.h>
 #include <rdline.h>
 #include <timer.h>
+#include <i2c.h>
 
 
+#include "../fpv-common/i2c_commands.h"
 #include "eeprom_config.h"
 #include "beep.h"
 #include "xbee_user.h"
 #include "eeprom_config.h"
 #include "beep.h"
 #include "xbee_user.h"
+#include "i2c_protocol.h"
 #include "main.h"
 
 struct xbeeboard xbeeboard;
 #include "main.h"
 
 struct xbeeboard xbeeboard;
@@ -166,6 +170,11 @@ int main(void)
        error_register_notice(mylog);
        error_register_debug(mylog);
 
        error_register_notice(mylog);
        error_register_debug(mylog);
 
+       /* I2C */
+       i2c_init(I2C_MODE_MASTER, I2C_MAINBOARD_ADDR);
+       i2c_protocol_init();
+       i2c_register_recv_event(i2c_recvevent);
+       i2c_register_send_event(i2c_sendevent);
 
        spi_servo_init();
        beep_init();
 
        spi_servo_init();
        beep_init();
diff --git a/main.h b/main.h
index 0f87a4f..7ee9ed7 100644 (file)
--- a/main.h
+++ b/main.h
@@ -47,6 +47,7 @@
 #define E_USER_DEFAULT           194
 #define E_USER_XBEE              195
 #define E_USER_RC_PROTO          196
 #define E_USER_DEFAULT           194
 #define E_USER_XBEE              195
 #define E_USER_RC_PROTO          196
+#define E_USER_I2C_PROTO         197
 
 #define LED1_ON()       sbi(PORTA, 2)
 #define LED1_OFF()      cbi(PORTA, 2)
 
 #define LED1_ON()       sbi(PORTA, 2)
 #define LED1_OFF()      cbi(PORTA, 2)
@@ -66,6 +67,7 @@
 #define BEEP_PRIO          120
 #define SPI_PRIO           100 /* users of spi_servo must have lower prio */
 #define XBEE_PRIO           80
 #define BEEP_PRIO          120
 #define SPI_PRIO           100 /* users of spi_servo must have lower prio */
 #define XBEE_PRIO           80
+#define I2C_PRIO            70
 #define LOW_PRIO            60
 /* lowest priority */
 
 #define LOW_PRIO            60
 /* lowest priority */