From 8f96511dd4a3a354a6a8c18bae5ade21b221fd95 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Thu, 17 Jul 2014 19:56:20 +0200 Subject: [PATCH] add i2c support --- Makefile | 1 + commands.c | 34 ++++++ i2c_protocol.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++++ i2c_protocol.h | 38 ++++++ main.c | 9 ++ main.h | 2 + 6 files changed, 407 insertions(+) create mode 100644 i2c_protocol.c create mode 100644 i2c_protocol.h diff --git a/Makefile b/Makefile index 3ff895f..7fcda32 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ SRC += parse_monitor.c SRC += cmdline.c SRC += beep.c SRC += eeprom_config.c +SRC += i2c_protocol.c CFLAGS += -W -Wall -Werror diff --git a/commands.c b/commands.c index c6a6cc9..befd1c9 100644 --- a/commands.c +++ b/commands.c @@ -48,6 +48,7 @@ #include "main.h" #include "cmdline.h" #include "beep.h" +#include "i2c_protocol.h" #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 */ @@ -2301,5 +2334,6 @@ const parse_ctx_t PROGMEM main_ctx[] = { &cmd_eeprom_add, &cmd_eeprom_add2, &cmd_eeprom_list, + &cmd_test, NULL, }; diff --git a/i2c_protocol.c b/i2c_protocol.c new file mode 100644 index 0000000..6734453 --- /dev/null +++ b/i2c_protocol.c @@ -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 +#include + +#include +#include +#include + +#include +#include + +#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 index 0000000..d101279 --- /dev/null +++ b/i2c_protocol.h @@ -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 --- 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 + * -> it failed but I answered y, then make reset and it was ok */ #include @@ -49,10 +50,13 @@ #include #include #include +#include +#include "../fpv-common/i2c_commands.h" #include "eeprom_config.h" #include "beep.h" #include "xbee_user.h" +#include "i2c_protocol.h" #include "main.h" struct xbeeboard xbeeboard; @@ -166,6 +170,11 @@ int main(void) 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(); diff --git a/main.h b/main.h index 0f87a4f..7ee9ed7 100644 --- 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_I2C_PROTO 197 #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 I2C_PRIO 70 #define LOW_PRIO 60 /* lowest priority */ -- 2.20.1