[tuxdroid-svn] r1110 - in firmware: tuxaudio/trunk tuxcore/trunk
Status: Beta
Brought to you by:
ks156
From: jaguarondi <c2m...@c2...> - 2008-05-06 09:38:41
|
Author: jaguarondi Date: 2008-05-06 11:38:42 +0200 (Tue, 06 May 2008) New Revision: 1110 Modified: firmware/tuxaudio/trunk/PC_communication.c firmware/tuxaudio/trunk/communication.c firmware/tuxaudio/trunk/communication.h firmware/tuxaudio/trunk/fifo.c firmware/tuxaudio/trunk/fifo.h firmware/tuxaudio/trunk/flash.c firmware/tuxaudio/trunk/flash.h firmware/tuxaudio/trunk/hardware.h firmware/tuxaudio/trunk/i2c.c firmware/tuxaudio/trunk/i2c.h firmware/tuxaudio/trunk/main.c firmware/tuxaudio/trunk/varis.c firmware/tuxaudio/trunk/varis.h firmware/tuxcore/trunk/communication.c firmware/tuxcore/trunk/communication.h firmware/tuxcore/trunk/global.c firmware/tuxcore/trunk/global.h firmware/tuxcore/trunk/i2c.c firmware/tuxcore/trunk/i2c.h firmware/tuxcore/trunk/main.c firmware/tuxcore/trunk/parser.c firmware/tuxcore/trunk/parser.h firmware/tuxcore/trunk/standalone.c Log: * Ported the refactored I2C developped for the production firmare to the public firmware. This is a major change in the internal communication between the 2 main MCUs. Earlier, the communication protocol on top of I2C was mutli-master, each MCU sending data as master to the other whenever necessary. Now, tuxaudio is a single master and regularly polls tuxcore for new status. The functionalities shouldn't have changed. This is a simple port, it seems to work but I didn't test it much. There are still many simplifications I want to do on the communication protocol. * Updated the fifo module and all calls. * Sleep functions added in the flash module. * Some cleanup. Modified: firmware/tuxaudio/trunk/PC_communication.c =================================================================== --- firmware/tuxaudio/trunk/PC_communication.c 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/PC_communication.c 2008-05-06 09:38:42 UTC (rev 1110) @@ -31,10 +31,9 @@ void spiTransaction(void) { - if ((spi_start) && (spi_enable)) // Wait start + if (spi_start) // Wait start { spi_start = 0; // Reset the spi start flag - spi_enable = 0; // Communication in progress spi_count = 0; // Reset spi counter spi_slave = HEADERS; // Set state machine spi_master = HEADERM; @@ -54,7 +53,7 @@ if (spi_slave == HEADERS) { /* Sound */ - if (fifoLength(&ADCFifo) >= 17) + if (FifoLength(ADCFifo) >= 17) spi_headerb = 0x02; /* frame will contain sound */ else spi_headerb = 0x00; /* no sound in frame */ @@ -78,8 +77,10 @@ spi_slave = PUT_COMMAND; // Next state if (spi_headerb & 0x02) { + uint8_t data; cli(); - SPDR = pullFifo(&ADCFifo); // Get data from FIFO + FifoGet(ADCFifo, &data); + SPDR = data; // Get data from FIFO sei(); } else @@ -135,11 +136,83 @@ { if (!lockAdaptFifo) { - adaptFifo(&PWMFifo); // Adaptative FIFO + //adaptFifo(PWMFifo); // Adaptative FIFO +#if 0 +/** \brief 3 levels adaptative function. + * \param p fifo pointer + * + * High and low thresholds are used to differentiate 3 levels of the buffer. In + * each level, a custom code or a function call can be provided to do the + * adaptation. + */ +void adaptFifo(fifo_t * p) +{ + unsigned char fifoLevel; + + fifoLevel = fifoLength(p); + if (!p->adpt_cycle) + { + if (p->matched == 8) + { + if (OCR0A < 254) + p->pwm_max = OCR0A + 2; + else + p->pwm_max = 255; + if (OCR0A > 231) + p->pwm_min = OCR0A - 2; + else + p->pwm_min = 230; + p->matched = 9; + } + + if (fifoLevel >= FIFO_ADAPT_HIGH) + { + if (OCR0A > p->pwm_min) + { + OCR0A--; + p->pwm_adpt_sens = 1; + } + } + else if (fifoLevel < FIFO_ADAPT_LOW) + { + if (OCR0A < p->pwm_max) + { + OCR0A++; + p->pwm_adpt_sens = 2; + } + } + else + { + if (p->pwm_adpt_sens) + { + if ((p->pwm_adpt_sens) == 1) + { + if (OCR0A < p->pwm_max) + OCR0A++; + } + else + { + if (OCR0A > p->pwm_min) + OCR0A--; + } + p->pwm_adpt_sens = 0; + p->matched = 0; + } + else + { + if (p->matched != 9) + p->matched++; + } + } + } + p->adpt_cycle++; + p->adpt_cycle &= (FIFO_ADAPT_RATE << 1) - 1; +} +#endif lockAdaptFifo = 1; } else - resetFifo(&PWMFifo); + FifoClear(PWMFifo); } else { @@ -153,7 +226,7 @@ if (spi_master_config & 0x02) { if (!flashPlay) - pushFifo(&PWMFifo, SPDR); // Put into the FIFO + FifoPut(PWMFifo, SPDR); // Put into the FIFO } if (spi_count == (spi_lenght_data + 1)) spi_master = READ_COMMAND; // Go to the next state @@ -177,7 +250,6 @@ rf_data_sent_ack = spi_commandRX[4]; /* get the acknowledge of the previous sent data */ PORTB |= 0x04; // Chip deselect - spi_enable = 1; break; } } Modified: firmware/tuxaudio/trunk/communication.c =================================================================== --- firmware/tuxaudio/trunk/communication.c 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/communication.c 2008-05-06 09:38:42 UTC (rev 1110) @@ -24,39 +24,24 @@ #include "communication.h" #include "i2c.h" +#include "hardware.h" +#include "config.h" -/* Debug mode, uncomment for debug */ -//#define __com_debug__ +#define I2C_TUXCORE_ADDR 0x2A -/* XXX i2c bug of the AVR */ -#define I2C_IDLE_RESET 250; -uint8_t i2c_idle_time = I2C_IDLE_RESET; +static uint8_t out_buf[CMD_SIZE]; +static struct i2c_msg msg_out = {0, 0, out_buf}; +static uint8_t in_buf[CMD_SIZE]; +static struct i2c_msg msg_in = {0, 0, in_buf}; +volatile uint8_t ret; +uint8_t statusFlag; -#ifdef __com_debug__ -uint8_t errorNbr = 0; -void comError(void) -{ - errorNbr = 1; -} -#endif - /* - * commandBuf is a filo buffer for commands received by RF and sent over i2c - * - * Check the following conditions when you fill it. - * - i2cFlags.i2c_idx == 0 : commandBuf is processed by the I2C sendCommands() - * function when i2c_idx = 1 so you should not change it at that time - * - this stack is a filo, you should push the latest byte first (latest - * parameter) so a command is always at the top of the stack. - * - * Note that because of this filo stack, commands are not processed in the - * order they were issued when the stack holds multiple commands. + * core_cmdout is a buffer for commands to be sent to tuxcore */ -uint8_t commandBuf[COMMAND_BUF_SIZE]; -fifo_t _commandFifo = { (uint8_t *) commandBuf, sizeof commandBuf - 1, 0, 0 } +FIFO_INSTANCE(core_cmdout_buf, COMMAND_BUF_SIZE); +fifo_t *core_cmdout = FifoPointer(core_cmdout_buf); -, *commandFifo = &_commandFifo; - /* * audioBuf is a single buffer for commands received over i2c to control the * audio interface @@ -66,7 +51,7 @@ * cleared when the command is processed so it can be used to check whether the * buffer has been processed or not. */ -uint8_t audioBuf[MAX_COMMAND_SIZE]; /* single buffer for commands received over i2c to control the audio interface */ +uint8_t audioBuf[CMD_SIZE]; /* single buffer for commands received over i2c to control the audio interface */ uint8_t audioBufIdx = 0; /* index indicating the number of valid bytes in the buffer */ /* @@ -74,31 +59,16 @@ * should be sent over the RF link * */ -uint8_t statusBuf[INCOMING_BUF_SIZE]; /* statusBuf is used for commands received from the i2c and should be sent over the RF link */ -fifo_t _statusBuf = { (uint8_t *) statusBuf, sizeof statusBuf - 1, 0, 0 } +FIFO_INSTANCE(statusFifo_s, INCOMING_BUF_SIZE); +fifo_t *statusFifo = FifoPointer(statusFifo_s); -, *statusFifo = &_statusBuf; - -void send_status(uint8_t status ,uint8_t byte1 ,uint8_t byte2 ,uint8_t byte3) -{ - uint8_t buf[4]; - buf[0] = status; - buf[1] = byte1; - buf[2] = byte2; - buf[3] = byte3; - cli(); - i2cSlaveReceiveService(4, buf); - sei(); -} - - /* - * Initializes (clear) the communication buffers + * Initialize (clear) the communication buffers */ void initCommunicationBuffers(void) { - resetFifo(commandFifo); + FifoClear(core_cmdout); audioBufIdx = 0; } @@ -110,183 +80,213 @@ */ void i2cCommunicationInit(void) { - i2cInit(); /* start twi interface */ - i2cSetSlaveReceiveHandler(i2cSlaveReceiveService); /* set receive function */ - i2cDeviceAddrRW = 0x54; /* set behaviour CPU device address */ - i2cFlags.m_end = 1; /* set master transmission end flag to enable the first transmission */ + i2c_init(); + msg_out.addr = I2C_TUXCORE_ADDR; + msg_out.buf = out_buf; + i2c_master_receive_handler(i2cMasterReceiveService); } /* * sendCommands takes one command (from 1 to 4 bytes) out of the command stack * and send them by i2c. * - * In order to protect this buffer of being corrupted by any modification in - * interrupts, a flag (i2cFlags.i2c_idx) is set during manipulations. - * - * The i2c busy flag (i2cFlags.i2c_busy) is checked here to know if we can send - * the next command. + * Should return 0 if nothing has to be sent. */ -void sendCommands(void) +int8_t sendCommands(void) { uint8_t i; + static uint8_t nack_cnt = 0; - if (!(i2cFlags.i2c_busy)) /* i2c hardware not busy */ + if (i2c_get_status() == I2C_BUSY) + return 1; + if (msg_out.state == I2C_NACK) { - i2c_idle_time = I2C_IDLE_RESET; - if (i2cFlags.mt_nack) /* if nack error, resend previous command */ + /* If NACK, try a couple more time. */ + if (++nack_cnt == 4) { - i2cMasterStart(); - cli(); - i2cFlags.mt_nack = 0; - i2cFlags.i2c_busy = 1; - sei(); + nack_cnt = 0; + switch (msg_out.addr) + { + case I2C_TUXCORE_ADDR: + /* Return 0 as if there were nothing to do so we can do + something else. Returning here doesn't drop the message. */ + return 0; + } } - else if (i2cFlags.m_end == 0) /* previous transmission was cancelled by arbitration lost or any problem so restart */ + else + i2c_send_bytes(&msg_out); + return 2; + } + else + nack_cnt = 0; + /* Send something else. */ + { + if (FifoLength(core_cmdout)) + /* Send commands received from RF or testers to tuxcore only. */ { - i2cMasterStart(); - cli(); - i2cFlags.i2c_busy = 1; - sei(); - } - else if (!isFifoEmpty(commandFifo)) /* elsif no error and previous transmission is finished and if there's something to send, get the next value */ - { - cli(); - i2cFlags.i2c_idx = 1; /* protect commandFifo from external changes */ - i2cFlags.m_end = 0; /* reset master transmission end flag */ - sei(); - i2cSendDataLength = MAX_COMMAND_SIZE; - for (i = 0; i < MAX_COMMAND_SIZE; i++) + msg_out.len = CMD_SIZE; + for (i = 0; i < CMD_SIZE; i++) { - if (popFifo(commandFifo, &i2cSendData[i])) - i2cSendDataLength = 0; /* drop the data if the fifo seems corrupted XXX add an error feedback on this */ + if (FifoGet(core_cmdout, &out_buf[i])) + /* Drop the data if the fifo is corrupted (not enough + * bytes) XXX add an error feedback on this */ + return -1; } - i2cMasterStart(); - cli(); - i2cFlags.i2c_idx = 0; /* release commandFifo protection */ - i2cFlags.i2c_busy = 1; - sei(); + msg_out.addr = I2C_TUXCORE_ADDR; + ret = i2c_send_bytes(&msg_out); } + else + /* Nothing to do anymore */ + return 0; } - /* XXX patch for the I2C bug of the AVR */ - else if (!i2c_idle_time--) + return 1; +} + +void getStatus(void) +{ + if (i2c_get_status() != I2C_BUSY) { - uint16_t wait = 0; - - TWCR = 0; - while (wait++ < 0xFFF0) ; - i2cFlags.i2c_busy = 0; - i2cFlags.m_end = 0; - i2cFlags.mt_nack = 0; - i2cInit(); + statusFlag = 0; + msg_in.addr = I2C_TUXCORE_ADDR; + msg_in.len = CMD_SIZE; + ret = i2c_read_bytes(&msg_in); } } + /* - * Insert a set of commands to the commandBuf filo stack. Returns '0' if the - * commands are stored, '1' if the stack is full and an error has been - * generated. - * - * i2cFlags.i2c_idx is not checked here so if you call this function from an - * interrupt you should check it yourself. - * - * The command[] parameter format starts with the command and then the parameters + * Add a command on the stack for tuxcore + * Returns 0 if the stack is full, 1 if the command has been added + * successfully. */ -uint8_t pushCommands(uint8_t * command) +int8_t send_core_cmd(uint8_t *cmd) { uint8_t i; - if (*command) - { - if (fifoLength(commandFifo) > (COMMAND_BUF_SIZE - MAX_COMMAND_SIZE)) /* Tux stack is full, we send an error back */ - return 1; - for (i = 0; i < MAX_COMMAND_SIZE; i++) - pushFifo(commandFifo, *command++); + if (FifoLength(core_cmdout) > FifoSize(core_cmdout) - CMD_SIZE) return 0; - } + + cli(); + for (i=0; i<CMD_SIZE; i++) + FifoPut(core_cmdout, cmd[i]); + sei(); return 1; } /* - * acceptData is a checking function associated with the i2c ISR to ack or nack - * the incoming command depending on the various stack status + * Add a command on the stack for tuxcore + * Returns 0 if the stack is full, 1 if the command has been added + * successfully. */ -void acceptData(void) +int8_t send_core_cmd_p(uint8_t cmd, uint8_t param1, uint8_t param2, \ + uint8_t param3) { - if (TWDR < 0xC0) /* command to be executed, cannot have 3 parameters */ - { - if (!audioBufIdx) - i2cFlags.s_val = 1; /* previous command processed */ - } - else if (fifoLength(statusFifo) < (INCOMING_FIFO_SIZE - MAX_COMMAND_SIZE)) - i2cFlags.s_val = 1; /* status with 3 parameters */ + uint8_t c[4] = {cmd , param1, param2, param3}; + return send_core_cmd(c); } -/* - * Slave receiver function associated with the i2c ISR - * - * Only do a backup of the buffer so any new i2c data won't overwrite the - * previous ones - */ -uint8_t pong_received; /* value of the pong received from the behavior */ -uint8_t pong_missed; /* counting the pong missed on the I2C */ +/* Send status to RF. */ +void send_status(uint8_t const *status) +{ + uint8_t i; -void i2cSlaveReceiveService(uint8_t receiveDataLength, uint8_t * receiveData) + if (FifoLength(statusFifo) <= INCOMING_BUF_SIZE - CMD_SIZE) + for (i = 0; i < CMD_SIZE; i++) + FifoPut(statusFifo, status[i]); + /* XXX what if stack is full? */ +} + +/* Send status to RF. */ +void send_status_p(uint8_t cmd, uint8_t param1, uint8_t param2, \ + uint8_t param3) { + uint8_t c[4] = {cmd , param1, param2, param3}; + send_status(c); +} + +uint8_t pong_received; /* value of the pong received from the behavior */ +uint8_t pong_missed; /* counting the pong missed on the I2C */ + +void parse_core_cmd(uint8_t *data) +{ uint8_t i; - if (*receiveData < 0xC0) /* (audio) command to be executed, can't have 3 parameters */ + if (*data < 0xC0) + /* Audio command to be executed, can't have 3 parameters */ { - for (i = 0; i < receiveDataLength; i++) - audioBuf[i] = receiveData[i]; - audioBufIdx = receiveDataLength; + for (i = 0; i < CMD_SIZE; i++) + audioBuf[i] = data[i]; + audioBufIdx = CMD_SIZE; /* XXX only fill buffer if it's not full, need to add the check */ } - else /* fill in the statusFifo buffer */ + else + /* Status, forward to RF and testers */ { + /* Intercept some of them */ /* Pong check */ - if (receiveData[0] == PONG_CMD) + if (data[0] == PONG_CMD) { - if (pong_received-- < receiveData[1]) /* new ping, reset */ + if (pong_received-- < data[1]) /* new ping, reset */ { - pong_received = receiveData[1]; + pong_received = data[1]; pong_missed = 0; } - else if (pong_received > receiveData[1]) /* pongs missed */ + else if (pong_received > data[1]) /* pongs missed */ { pong_missed++; - pong_received = receiveData[1]; /* resync */ + pong_received = data[1]; /* resync */ } - receiveData[2] = pong_missed; + data[2] = pong_missed; } + send_status(data); + } +} - /* Store data on the fifo buffer */ - if (fifoLength(statusFifo) < INCOMING_FIFO_SIZE - MAX_COMMAND_SIZE) - for (i = 0; i < MAX_COMMAND_SIZE; i++) - pushFifo(statusFifo, receiveData[i]); - /* XXX add a return in case data was not added */ +/* + * Slave receiver function associated with the i2c ISR + * + * Only do a backup of the buffer so any new i2c data won't overwrite the + * previous ones + */ +void i2cMasterReceiveService(uint8_t receiveDataLength, uint8_t * receiveData) +{ + if (*receiveData == 0) + /* Nothing to get. */ + return; + if (receiveDataLength != CMD_SIZE) + /* Error here. */ + return; + if (msg_in.addr == I2C_TUXCORE_ADDR) + /* From tuxcore */ + { + /* Parse commands */ + parse_core_cmd(receiveData); + /* If we got something, there's maybe more so continue. */ + statusFlag = 1; } } /* * Get a set of status commands from the statusFifo buffer. The command - * length will always be MAX_COMMAND_SIZE. Returns '1' if there's nothing to + * length will always be CMD_SIZE. Returns '1' if there's nothing to * get, '0' otherwise. * * The command[] parameter format starts with the command and then the parameters */ -uint8_t popStatus(uint8_t * command) +uint8_t popStatus(uint8_t *command) { uint8_t i; - if (isFifoEmpty(statusFifo)) + if (!FifoLength(statusFifo)) return 1; /* nothing to do */ cli(); /* XXX try to disable I2C interrupts instead */ - for (i = 0; i < MAX_COMMAND_SIZE; i++) - if (popFifo(statusFifo, &command[i])) + for (i = 0; i < CMD_SIZE; i++) + if (FifoGet(statusFifo, &command[i])) { sei(); - return 1; /* fifo corrupted so drop data XXX add some debug feedback on this instead of dropping data */ + return 1; /* fifo corrupted so drop data XXX add some debug + feedback on this instead of dropping data */ } sei(); return 0; Modified: firmware/tuxaudio/trunk/communication.h =================================================================== --- firmware/tuxaudio/trunk/communication.h 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/communication.h 2008-05-06 09:38:42 UTC (rev 1110) @@ -22,34 +22,47 @@ #ifndef COMMUNICATION_H #define COMMUNICATION_H +#include <stdbool.h> + #include "common/commands.h" +#include "common/api.h" +#include "common/defines.h" +#include "varis.h" #include "fifo.h" #define COMMAND_BUF_SIZE 16 #define COMMAND_FIFO_SIZE (COMMAND_BUF_SIZE - 1) /* due to the fifo construction */ -#define MAX_COMMAND_SIZE 4 +#define CMD_SIZE 4 #define INCOMING_BUF_SIZE 32 #define INCOMING_FIFO_SIZE (INCOMING_BUF_SIZE - 1) /* due to the fifo construction */ -extern uint8_t audioBuf[MAX_COMMAND_SIZE]; /* single buffer for commands received over i2c to control the audio interface */ +extern uint8_t audioBuf[CMD_SIZE]; /* single buffer for commands received over i2c to control the audio interface */ extern uint8_t audioBufIdx; /* index indicating the number of valid bytes in the buffer */ +extern uint8_t statusFlag; + extern fifo_t *statusFifo; /* incomingBuf is used for commands received from the i2c and should be sent over the RF link */ +/* /< XXX New clean commands */ +int8_t send_core_cmd(uint8_t *command); +int8_t send_core_cmd_p(uint8_t command, uint8_t param1, uint8_t param2, \ + uint8_t param3); +/* XXX /> */ + void i2cCommunicationInit(void); void initCommunicationBuffers(void); /* From RF to i2c */ -void sendCommands(void); -uint8_t pushCommands(uint8_t * command); +int8_t sendCommands(void); + /* From i2c to RF */ void acceptData(void); -void i2cSlaveReceiveService(uint8_t receiveDataLength, uint8_t * receiveData); -void send_status(uint8_t status ,uint8_t byte1 ,uint8_t byte2 ,uint8_t byte3); +void i2cMasterReceiveService(uint8_t receiveDataLength, uint8_t * receiveData); +void send_status(uint8_t const *status); +void send_status_p(uint8_t cmd, uint8_t param1, uint8_t param2, \ + uint8_t param3); uint8_t popStatus(uint8_t * command); +void getStatus(void); -/* XXX move to a global definition file */ -#define AUDIO_STATE 0xE0 /* state of the audio interface */ - #endif /* COMMUNICATION_H */ Modified: firmware/tuxaudio/trunk/fifo.c =================================================================== --- firmware/tuxaudio/trunk/fifo.c 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/fifo.c 2008-05-06 09:38:42 UTC (rev 1110) @@ -19,228 +19,63 @@ /* $Id$ */ -#include <avr/io.h> -#include <inttypes.h> -#include "fifo.h" - /** \file fifo.c - \ingroup single_size_fifo - \brief Standard pointer fifo - - \section modulo Typecasts and modulo - - \note All wrap around now use '&' instead of '\%' because the size is a - variable and not a constant and the compiler can't optimize it itself - anymore. I still leave this notice for reference and in case someone wants - to change back to modulo. - - All functions that use modulo usually use typecasts - i.e. the following statement - - \code length = (p->inIdx - p->outIdx) % p->size; \endcode - - has a problem when (p->inIdx - p->outIdx) is <0. Then the - modulus is not done, the return value is 0xF9 for example. This is due to - the implicit promotion of the intermediate value to an int. - - "%" is a signed modulus, so -1 % 8 == -1. - - We need to trim down the intermediate result to a uint8_t result - immediately so the modulus applies to a uint8_t otherwise the implicit - promotion of a uint8_t argument to an int expression makes the expression - signed and brake the code for the above reason. The correct expression is - - \code length = ((uint8_t)(p->inIdx - p->outIdx) % p->size); \endcode - - So forgetting the typecast not only makes the code much bigger, it also - brake it when the intermediate result is negative. - + \brief Circular buffer (FIFO) */ -/** \name Accessors +#include <inttypes.h> +#include "fifo.h" - Functions to initialise, manipulate and get the status of the fifo buffer. */ +/** \weakgroup circular_buffer */ +/*! @{ */ -/* @{ */ -/** \ingroup single_size_fifo */ - -/** \brief Resets the indexes to an empty state (8b). - * \param p fifo pointer - * - * Note that the first element in the fifo will be stored in p->buffer[1] and not p->buffer[0] */ -void resetFifo(fifo_t * p) +/** \brief Empty the buffer by clearing the indexes. + * \param p Fifo pointer. + */ +void FifoClear(fifo_t *p) { p->inIdx = 0; p->outIdx = 0; - p->pwm_adpt_sens = 0; - p->adpt_cycle = 0; - p->matched = 0; - p->pwm_min = 230; - p->pwm_max = 255; } -/** \brief Return TRUE if the fifo buffer is full (28b). - * \param p fifo pointer - * \return TRUE if fifo buffer is full +/** \brief Return TRUE if the buffer is full. + * \param p Fifo pointer. + * \return TRUE if the buffer is full, FALSE otherwise. */ -uint8_t isFifoFull(fifo_t * p) +uint8_t FifoFull(fifo_t const *p) { - return (p->outIdx == (((uint8_t) (p->inIdx + 1)) & p->size)); /* typecast necessary because the sum is promoted to an int and without the type cast we can have negative values which after the modulo are still negative values. See modulo section above */ + return (FifoLength(p) == p->size); } -/** \brief Return TRUE if the fifo buffer is empty (22b). - * \param p fifo pointer - * \return TRUE if fifo buffer is empty +/** \brief Add one data byte to the fifo buffer. + * \param p Fifo pointer. + * \param data Data byte to add to the queue. + * \return Return FIFO_OK if the data has been added, FIFO_FULL if the buffer + * was full and the data couldn't be added. */ -uint8_t isFifoEmpty(fifo_t * p) +int8_t FifoPut(fifo_t *p, uint8_t const data) { - return (p->outIdx == p->inIdx); -} + if (FifoLength(p) == p->size) + return FIFO_FULL; -/** \brief Return the number of elements in the fifo buffer (16b). - * \param p fifo pointer - * \return number of elements in the buffer - */ -uint8_t fifoLength(fifo_t * p) -{ - return ((uint8_t) (p->inIdx - p->outIdx)) & p->size; /* typecast necessary because the sum is promoted to an int and without the type cast we can have negative values which after the modulo are still negative values. See modulo section above */ + p->buffer[p->inIdx++ & (p->size-1)] = data; /* store data */ + return FIFO_OK; } -/** \brief Add one data byte to the fifo buffer (32b). - * \param p fifo pointer - * \param data data byte to add to the queue - * - * If the fifo is already full, return immediately without doing anything. +/** \brief Pop the oldest byte from the buffer. + * \param p Fifo pointer. + * \param data pointer for storing the data read from the queue + * \return FIFO_OK if a value has been popped out at the pointer address. If + * the fifo is empty, FIFO_EMPTY is returned and the pointed data is left + * unchanged. */ -void pushFifo(fifo_t * p, uint8_t data) +int8_t FifoGet(fifo_t *p, uint8_t *data) { - uint8_t inIdx; /* use of a local variable to reduce code size */ + if (p->outIdx == p->inIdx) + return FIFO_EMPTY; - inIdx = ((uint8_t) (p->inIdx + 1)) & p->size; /* typecast necessary because the sum is promoted to an int and without the type cast we can have negative values which after the modulo are still negative values. See modulo section above */ - if (p->outIdx != inIdx) /* full if p->outIdx == p->inIdx + 1 % wrap-around, if not full: */ - { - p->inIdx = inIdx; /* ++ index */ - p->buffer[inIdx] = data; /* stora data */ - } + *data = p->buffer[p->outIdx++ & (p->size-1)]; + return FIFO_OK; } -/** \brief Pop the oldest byte in the fifo (48b). - * \param p fifo pointer - * \param data pointer to store the data read from the queue - * \return success status - * - * Return '0' if a value has been popped out at the pointer address. If the - * fifo is empty, the index is not decreased, '1' is returned and the pointed - * data is left unchanged. - */ -uint8_t popFifo(fifo_t * p, uint8_t * data) -{ - if (p->outIdx != p->inIdx) /* if fifo is not empty */ - { - p->outIdx++; /* ++ index */ - p->outIdx = p->outIdx & p->size; /* wrap-around */ - *data = p->buffer[p->outIdx]; /* get data */ - return 0; - } - else - return 1; -} - -/** \brief Pull the oldest byte from the fifo, always return something even if - * the fifo is empty (36b). - * \param p fifo pointer - * \return data read from the queue - * - * If the fifo is empty, the index is not decreased but the latest value is - * returned. - */ -uint8_t pullFifo(fifo_t * p) -{ - if (p->outIdx != p->inIdx) /* if fifo is not empty */ - { - p->outIdx++; /* ++ index */ - p->outIdx = p->outIdx & p->size; /* wrap-around */ - } - return p->buffer[p->outIdx]; /* get data in all cases */ -} - -/*@}*/ - -/** - \name Adaptative functions - @{ -*/ -/** \ingroup single_size_fifo */ - -/** \brief 3 levels adaptative function. - * \param p fifo pointer - * - * High and low thresholds are used to differentiate 3 levels of the buffer. In - * each level, a custom code or a function call can be provided to do the - * adaptation. - */ -void adaptFifo(fifo_t * p) -{ - unsigned char fifoLevel; - - fifoLevel = fifoLength(p); - if (!p->adpt_cycle) - { - if (p->matched == 8) - { - if (OCR0A < 254) - p->pwm_max = OCR0A + 2; - else - p->pwm_max = 255; - if (OCR0A > 231) - p->pwm_min = OCR0A - 2; - else - p->pwm_min = 230; - p->matched = 9; - } - - if (fifoLevel >= FIFO_ADAPT_HIGH) - { - if (OCR0A > p->pwm_min) - { - OCR0A--; - p->pwm_adpt_sens = 1; - } - } - else if (fifoLevel < FIFO_ADAPT_LOW) - { - if (OCR0A < p->pwm_max) - { - OCR0A++; - p->pwm_adpt_sens = 2; - } - } - else - { - if (p->pwm_adpt_sens) - { - if ((p->pwm_adpt_sens) == 1) - { - if (OCR0A < p->pwm_max) - OCR0A++; - } - else - { - if (OCR0A > p->pwm_min) - OCR0A--; - } - p->pwm_adpt_sens = 0; - p->matched = 0; - } - else - { - if (p->matched != 9) - p->matched++; - } - } - } - p->adpt_cycle++; - p->adpt_cycle &= (FIFO_ADAPT_RATE << 1) - 1; -} - -/*@}*/ +/*! @} */ Modified: firmware/tuxaudio/trunk/fifo.h =================================================================== --- firmware/tuxaudio/trunk/fifo.h 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/fifo.h 2008-05-06 09:38:42 UTC (rev 1110) @@ -19,137 +19,144 @@ /* $Id$ */ -#ifndef FIFO_H -#define FIFO_H +/** \file fifo.h + \ingroup circular_buffer + \brief Circular buffer (FIFO) -/** \defgroup single_size_fifo Single size Fifo - \ingroup fifo + \section Internals - "fifo" is a general use fifo wich has been optimized for size. The fifo is - a structure of a data array pointer, it's size and input and output - indexes. The fifo buffer is never accessed directly by the application. - Therefore it has a couple of accessors to push/pop data or access - properties like status or length. + These routines are inspired from an article in Jack Ganssle's Embedded + Muse: http://www.ganssle.com/tem/tem110.pdf - All fifo sizes should have a value to the power of 2 so the wrap-around - (modulo), necessary when the index is at the end of the buffer array, - simplifies to a single AND operation. This greatly reduces the code size. - Maximum fifo size is 256. It is not possible to use any value for the fifo - size although this could be done by changing all '&' operations by '%' in - fifo.c but the code size will increase a lot. It would be more efficient to - change the wrap around to an 'if (inIdx == outIdx)' method. + The buffer size must be a power of 2 and is limited to 128. This simplyfies + the wrap-around to a single AND operation which is only applied when + accessing the buffer but not applied on the in and out indexes. The indexes + increase from 0 to 255 then wrap to 0. With this method, the buffer is + empty when the indexes are equal and is full when the difference is equal + to the buffer size. Thus we can't use a buffer of 256 as it wouldn't be + possible to differentiate the buffer when it's empty or full. +*/ - Note that the buffer can only hold the given size minus 1 (fifo->size - - 1) elements to be able to make the difference between completely full and - completely empty states. So if your fifo has a size of 128, it'll only hold - a maximum of 127 elements. +#ifndef _FIFO_H_ +#define _FIFO_H_ - Interrupts should be disabled before a call to an accessor if fifo's are - also manupilated in interrupts. It's up to the application to take care of - this. As all accessors are very short, it makes no sense to have the sei - and cli inside them, they would be at the beginning and end of the function - anyway. +/** \defgroup circular_buffer Circular buffer (FIFO) + This circular buffer module implements a minimal general use fifo. + + The fifo is a structure of a data array pointer, it's size, input and + output indexes. The fifo buffer itself is hidden from the + application. There are a couple of functions to put/get data or + access its properties. + + The buffer size must be a power of 2 and is limited to 128 (2, 4, + 8, 16, 32, 64, or 128). + + Interrupts should be disabled if you access a fifo from both interrupt + routines and the main code. + Usage: - use the following include: \code #include "fifo.h" \endcode - - create a buffer, a fifo structure and a pointer to it. The structure - should be initialized with a pointer to the buffer, the (size - 1) of the - buffer (see the structure definition for details) and input and output - indexes which should be reset. Replace FIFO_SIZE by a value or define it. - \code uint8_t testFifo_buf[FIFO_SIZE]; - fifo_t testFifo = {testFifo_buf, sizeof testFifo_buf-1, 0, 0}, *myFifo = &testFifo; + - instanciate a fifo with a given name and size. The size must be a power + of 2 and is limited to 128 (2, 4, 8, 16, 32, 64, or 128). + \code + FIFO_INSTANCE(fifo_name, FIFO_SIZE); \endcode + - declare a pointer to a fifo type and initialize it with the address of + the fifo you just created + \code + fifo_t *myFifo = FifoPointer(fifo_name); + \endcode - If you want multiple fifo's, just repeat the last step with other names for - the buffer, fifo and pointer. + You can instanciate multiple fifo with FIFO_INSTANCE and get the pointer to + them with FifoPointer(). +*/ - Test code: test_fifo.c +/* + * Hidden from the interface + */ - \section Internals +/** \brief Fifo structure type which holds the buffer, it's size, input and + * output indexes. + * + * This structure is hidden from the application and should not be accessed + * directly. + */ +typedef struct fifo_t fifo_t; +struct fifo_t { + /** buffer array */ + uint8_t *buffer; + /** size of the buffer */ + uint8_t const size; + /** input index, points to the next empty cell */ + uint8_t inIdx; + /** output index, points to the next value to get */ + uint8_t outIdx; +}; - fifo_s structure holds the stack buffer, it's size and 2 indexes. The input index - points to the last pushed value and the output index points to the last - popped value. +/** \addtogroup circular_buffer */ +/*! @{ */ - The fifo is empty when both indexes are equal which means that the last - pushed value has been popped. The fifo is full when the output index is - the input index +1. There's still one free space in the fifo when it's full - but we can't use it while keeping the fifo handling simple. +/** \brief Status which is returned by some functions. */ +enum FIFO_STATUS {FIFO_OK = 0, FIFO_FULL, FIFO_EMPTY}; - Here's a diagram that shows how indexes change while the fiffo is filled - and emptied. The fifo is 8 bytes long and indexes are intialized at 0. +/** \name Initialization */ +/*! @{ */ - \image html fifo_fifostates.png "Fifo states" +/* + * Initialization + */ -*/ +/** \brief This macro instanciates a fifo given its name and its size. The fifo + * can then be accessed with FifoPointer(). + * + * The macro declares the fifo buffer and a the fifo structure which holds the + * buffer, the size and the indexes. Using this macro somehow hides the + * structure from the application. + * + * The real buffer is named with the fifo name appended with _buf and the fifo + * structure is named with the fifo name appended with _struct. + */ +#define FIFO_INSTANCE(fifo_name, fifo_size) \ + uint8_t fifo_name##_buf[fifo_size]; \ + fifo_t fifo_name##_struct = {fifo_name##_buf, sizeof fifo_name##_buf, 0, 0} -/** \file fifo.h - \ingroup single_size_fifo - \brief Single size fifo +/** \brief Return the address of the fifo + * \param fifo_name Name of the fifo you defined with FIFO_INSTANCE. + * \return pointer to the fifo + * + * As the structure should be hidden from the application, this macro should + * be used to return the address of a given fifo. + */ +#define FifoPointer(fifo_name) (&fifo_name##_struct) -*/ +/*! @} */ +/** \name Interface */ +/*! @{ */ /* - * Adaptive parameters + * Interface */ -/** \brief UD- Rate at which the adaptation is done. - * - * The adaptation function will only be run each 2^FIFO_ADAPT_RATE sample */ -#define FIFO_ADAPT_RATE 1 /* adaptation is done each 2^FIFO_ADAPT_RATE sample */ -/** \brief UD- High level threshold */ -#define FIFO_ADAPT_HIGH 64 // (p->size - (p->size>>2)) /* 3/4 of size */ +/** \brief Return the number size of the fifo buffer. + * \param p Fifo pointer. + * \return Size of the buffer. + */ +#define FifoSize(p) (p->size) -/** \brief UD- Low level threshold */ -#define FIFO_ADAPT_LOW 17 // (p->size>>2) /* 1/4 of size */ +void FifoClear(fifo_t *p); +uint8_t FifoFull(fifo_t const *p); -#define FIFO_MATCHED_THRESHOLD 8 - -/** \brief Fifo structure type which holds the buffer, it's size, input and - * output indexes. - - Note that the buffer can only hold (size - 1) elements to be able to - make the difference between completely full and completely empty states. - */ -typedef struct fifo_s -{ - /** array that will store the elements */ - uint8_t *buffer; - /** size of the fifo buffer array minus 1 - * The wrap around is made by a 'AND' function so the AND should be made with - * the (array size -1) to be able to clear all bits above the array size. - * This -1 is not related to the fact that the buffer can only hold (size -1) - * elements. */ - uint8_t size; - /** input index which indexes the last pushed value */ - uint8_t inIdx; - /** output index which indexes the last popped value */ - uint8_t outIdx; - /** current sens : increment or decrement pwm value */ - uint8_t pwm_adpt_sens; - /** cycle counter of adaptative function */ - uint8_t adpt_cycle; - /** counter of good pwm value */ - uint8_t matched; - /** min value of pwm range */ - uint8_t pwm_min; - /** max value of pwm range */ - uint8_t pwm_max; - -} fifo_t; - -/* - * Extern declarations +/** \brief Return the number of elements in the fifo buffer. + * \param p Fifo pointer. + * \return Number of elements in the buffer. */ +#define FifoLength(p) (uint8_t)(p->inIdx - p->outIdx) -void resetFifo(fifo_t * p); -uint8_t isFifoFull(fifo_t * p); -uint8_t isFifoEmpty(fifo_t * p); -uint8_t fifoLength(fifo_t * p); -void pushFifo(fifo_t * p, uint8_t data); -uint8_t popFifo(fifo_t * p, uint8_t * data); -uint8_t pullFifo(fifo_t * p); -void adaptFifo(fifo_t * p); +int8_t FifoPut(fifo_t *p, uint8_t const data); +int8_t FifoGet(fifo_t *p, uint8_t *data); -#endif /* FIFO_H */ +/*! @} */ +/*! @} */ +#endif /* _FIFO_H_ */ Modified: firmware/tuxaudio/trunk/flash.c =================================================================== --- firmware/tuxaudio/trunk/flash.c 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/flash.c 2008-05-06 09:38:42 UTC (rev 1110) @@ -144,19 +144,19 @@ ad[2] = 0; first_block = (ad[0] << 4) + (ad[1] >> 4); } - resetFifo(&PWMFifo); + FifoClear(PWMFifo); if (ad[0] > 0x07) { programming_state = PROG_END; - send_status(STATUS_FLASH_PROG_CMD, FLASH_FULL, 0, 0); + send_status_p(STATUS_FLASH_PROG_CMD, FLASH_FULL, 0, 0); } else programming_state ++; frame_without_sound = 5000; frame_without_sound_timeout = 5000; - send_status(STATUS_FLASH_PROG_CMD, IN_PROGRESS, ad[0], ad[1]); + send_status_p(STATUS_FLASH_PROG_CMD, IN_PROGRESS, ad[0], ad[1]); } else if (programming_state == PROG_INIT) @@ -177,12 +177,12 @@ if (sound_stored) { last_block = (ad[0] << 4) + (ad[1] >> 4); - send_status(STATUS_FLASH_PROG_CMD, WAITING_FOR_CONFIRMATION, last_block - first_block, 0); + send_status_p(STATUS_FLASH_PROG_CMD, WAITING_FOR_CONFIRMATION, last_block - first_block, 0); programming_state ++; } else { - send_status(STATUS_FLASH_PROG_CMD, NO_SOUND, 0, 0); + send_status_p(STATUS_FLASH_PROG_CMD, NO_SOUND, 0, 0); programming_state = PROG_END; } } @@ -195,7 +195,7 @@ } else if (write_toc == 2) { - send_status(STATUS_FLASH_PROG_CMD, ERASING_LAST_SOUND, 0, 0); + send_status_p(STATUS_FLASH_PROG_CMD, ERASING_LAST_SOUND, 0, 0); if (first_block == 0) { eraseFlag = 1; @@ -214,7 +214,7 @@ } else if (programming_state == PROG_TOC) { - send_status(STATUS_FLASH_PROG_CMD, WRITE_TOC, 0, 0); + send_status_p(STATUS_FLASH_PROG_CMD, WRITE_TOC, 0, 0); numSound ++; index = (numSound * 3) + 1; program_flash(0x00, (index>>8), (index & 0xFF), ad[0]); @@ -233,12 +233,41 @@ programmingFlash = 0; TIMSK0 = 0x01; //info_flg = 1; - send_status(STATUS_FLASH_PROG_CMD, STANDBY, 0, 0); - send_status(SOUND_VAR_CMD, numSound, last_block, 0); + send_status_p(STATUS_FLASH_PROG_CMD, STANDBY, 0, 0); + send_status_p(SOUND_VAR_CMD, numSound, last_block, 0); } } /** + * \ingroup flash + * \brief Active the deep power-down mode + * + * This mode active the deep-power mode. The consumption will pass to 8mA from + * 20uA. + */ +void enter_deep_sleep(void) +{ + flash_enable(); + flash_select(); + spiSend(DEEP_POWER_MODE); + flash_unselect(); +} + +/** + * \ingroup flash + * \brief Leave the deep power_down mode + * + * This function will resume the flash memory from the deep power-down mode. + */ +void leave_deep_sleep(void) +{ + flash_enable(); + flash_select(); + spiSend(RESUME_DEEP_MODE); + flash_unselect(); +} + +/** * \ingroup flash \brief Erase the flash memory. @@ -256,7 +285,7 @@ } else if (!(read_status() & BUSY)) { - //send_status(STATUS_FLASH_PROG_CMD, STANDBY, 0, 0); + //send_status_p(STATUS_FLASH_PROG_CMD, STANDBY, 0, 0); enter = 1; eraseFlag = 0; program_flash(0x00, 0x00, 0x00, 0xFE); @@ -265,8 +294,8 @@ program_flash(0x00, 0x00, 0x03, 0x00); numSound = 0; last_block = 0; - send_status(STATUS_FLASH_PROG_CMD, STANDBY, 0, 0); - send_status(SOUND_VAR_CMD, numSound, last_block, 0); + send_status_p(STATUS_FLASH_PROG_CMD, STANDBY, 0, 0); + send_status_p(SOUND_VAR_CMD, numSound, last_block, 0); //info_flg = 1; TIMSK0 = 0x01; } @@ -397,7 +426,7 @@ } } } - resetFifo(&PWMFifo); + FifoClear(PWMFifo); flash_select(); // Chip Select spiSend(0x03); // Send Read Page Command @@ -421,11 +450,11 @@ static void playingSound(void) { uint8_t sound; - while (!spi_start && !isFifoFull(&PWMFifo)) + while (!spi_start && !FifoFull(PWMFifo)) { sound = spiSend(0x00); // Wait response sound = sound >> audioLevel; - pushFifo(&PWMFifo, sound); + FifoPut(PWMFifo, sound); ad[2]++; // Increment address for next play if (ad[2] == 0) @@ -461,7 +490,7 @@ { soundToPlay = 0; flashPlay = 0; - send_status(STATUS_AUDIO_CMD, 0, 0, 0); + send_status_p(STATUS_AUDIO_CMD, 0, 0, 0); PORTB |= 0x01; // Set the HOLD signal PORTB |= 0x02; // Chip Deselect } @@ -475,17 +504,18 @@ The second step is to send the OP code and a data to write. This function perform the first step. - */ + */ static void init_programming(uint8_t adi0, uint8_t adi1, uint8_t adi2) { + uint8_t data; write_enable(); - flash_select(); - spiSend(SEQU_PROGRAM); - spiSend(adi0); + flash_select(); + spiSend(SEQU_PROGRAM); + spiSend(adi0); spiSend(adi1); spiSend(adi2); - if (!isFifoEmpty(&PWMFifo)) - spiSend(pullFifo(&PWMFifo)); + if (FifoGet(PWMFifo, &data) == FIFO_OK) + spiSend(data); else spiSend(0x80); @@ -518,15 +548,17 @@ { while (!spi_start) { - if (!isFifoEmpty(&PWMFifo)) + if (FifoLength(PWMFifo)) { + uint8_t data; sound_stored = 1; frame_without_sound = STOP_FRAME_NUMBER; frame_without_sound_timeout = STOP_FRAME_NUMBER; flash_select(); spiSend(SEQU_PROGRAM); - spiSend(pullFifo(&PWMFifo)); + FifoGet(PWMFifo, &data); + spiSend(data); flash_unselect(); ad[2] ++; Modified: firmware/tuxaudio/trunk/flash.h =================================================================== --- firmware/tuxaudio/trunk/flash.h 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/flash.h 2008-05-06 09:38:42 UTC (rev 1110) @@ -73,6 +73,8 @@ extern void erase(void); extern uint8_t readFlashNumber(void); extern uint8_t readLastBlock(uint8_t num); +extern void enter_deep_sleep(void); +extern void leave_deep_sleep(void); /** start / end flash states flag */ extern uint8_t flash_state; Modified: firmware/tuxaudio/trunk/hardware.h =================================================================== --- firmware/tuxaudio/trunk/hardware.h 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/hardware.h 2008-05-06 09:38:42 UTC (rev 1110) @@ -24,6 +24,24 @@ #ifndef _HARDWARE_H_ #define _HARDWARE_H_ +/** VCC mask. */ +#define VCC_MK _BV(PD6) +/** VCC PIN. */ +#define VCC_PIN PIND +/** VCC PORT. */ +#define VCC_PT PORTD +/** VCC DDR. */ +#define VCC_DDR DDRD + +/** RF reset mask. */ +#define RF_RESET_MK _BV(PB7) +/** RF reset PIN. */ +#define RF_RESET_PIN PINB +/** RF reset PORT. */ +#define RF_RESET_PT PORTB +/** RF reset DDR. */ +#define RF_RESET_DDR DDRB + /* Flash memory port */ #define FLASH_PORT PORTB #define FLASH_CS_PIN _BV(PB1) Modified: firmware/tuxaudio/trunk/i2c.c =================================================================== --- firmware/tuxaudio/trunk/i2c.c 2008-05-06 09:27:01 UTC (rev 1109) +++ firmware/tuxaudio/trunk/i2c.c 2008-05-06 09:38:42 UTC (rev 1110) @@ -19,122 +19,92 @@ /* $Id$ */ +/*@{*/ #include <avr/io.h> #include <avr/interrupt.h> #include "i2c.h" -#include "communication.h" +#define TWI_TWCR (_BV(TWINT) | _BV(TWIE) | _BV(TWEN)) + /* + * I2C constants. + */ +/** Value of the R/W bit when sending the slave address in read mode. */ +#define I2C_R_BIT 1 +/** Value of the R/W bit when sending the slave address in write mode. */ +#define I2C_W_BIT 0 + +/* * DEBUG and TEST flags */ -#define __i2c_debug__ 0 /* stores in a ring buffer all I2C status that occurs */ -#define __i2c_signals__ 0 /* outputs some signals on pins, see below */ +/* If set, store in a ring buffer all I2C status that occur. */ +#define __i2c_debug__ 0 +/* If set, some signals will be output on 3 pins, see below. */ +#define __i2c_signals__ 0 /* DEBUG definitions and variables */ #if (__i2c_debug__) -uint8_t i2cStatus[32]; /* debug only */ -uint8_t i2cStatusIdx = 0; -extern uint8_t i2cStatus[]; /* needs to be here for AVRStudio to show it in watch windows */ -extern uint8_t i2cStatusIdx; +#define I2C_DBG_STACK_SIZE 16 +/* Debug stack, it's a ring buffer that hold all TWI status codes. */ +uint8_t twi_debug_stack[I2C_DBG_STACK_SIZE]; +uint8_t twi_debug_stack_idx = 0; #endif #if (__i2c_signals__) -/* The START signal is set when a start is requested and reset when the TWI interrupt happens. Either the start is issued and the signal is reset when the interrupt occurs, or the TWI bus was already busy and the signal is only reset when the SLA+W interrupt will happen and no start will be issued this time */ -#define START_PT PORTD -#define START_MK 0x01 +/* The START signal is set when a start is requested and reset when the TWI + * interrupt happens. Either the start is issued and the signal is reset when + * the interrupt occurs, or the TWI bus was already busy and the signal is only + * reset when the SLA+W interrupt will happen and no start will be issued this + * time */ +#define START_PT PORTC +#define START_MK 0x04 #define START_DDR DDRD /* The ISR signal is set at the beginning of the TWI ISR and reset at the end of the interrupt */ -#define ISR_PT PORTD -#define ISR_MK 0x02 +#define ISR_PT PORTC +#define ISR_MK 0x08 #define ISR_DDR DDRD -/* The BUSY signal is the same as i2cFlags.i2c_busy and is set when a start is issued or an TWI slave address is recognized. It is reset at the end of communication when a stop is sent or received */ +/* The BUSY signal is the same as I2C_BUSY and is set when a start is issued or an TWI slave address is recognized. It is reset at the end of communication when a stop is sent or received */ #define BUSY_PT PORTD #define BUSY_MK 0x80 #define BUSY_DDR DDRD -#endif +#endif /* __i2c_signals__ */ /* I2C status and address variables */ -uint8_t i2cDeviceAddrRW; -volatile I2C_FLAGS i2cFlags; /* this is a bitfield and it takes more than 1 cycle to set a bit so disable interrupts before changing it outside interrupts */ +/** High level status of the I2C module. */ +static enum i2c_state i2c_state; +/** Pointer to the currently processed I2C message. */ +static struct i2c_msg *m_msg; +/** SLA+R/W value. */ +static uint8_t sla_rw; +/** Index for the message buffer. */ +static volatile uint8_t buf_idx; -/* send/transmit buffer (outgoing data) */ -uint8_t i2cSendData[I2C_SEND_DATA_BUFFER_SIZE]; -uint8_t i2cSendDataIndex; -uint8_t i2cSendDataLength; - -/* receive buffer (incoming data) */ -uint8_t i2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE]; -uint8_t i2cReceiveDataIndex; -uint8_t i2cReceiveDataLength; - /* function pointer to i2c receive routine */ /* I2cSlaveReceive is called when this processor is addressed as a slave for * writing */ -static void (*i2cSlaveReceive) (uint8_t receiveDataLength, +static void (*i2c_master_receive) (uint8_t receiveDataLength, uint8_t * recieveData); /* I2cSlaveTransmit is called when this processor is addressed as a slave for * reading */ static uint8_t(*i2cSlaveTransmit) (uint8_t transmitDataLengthMax, uint8_t * transmitData); -/* functions */ -/* - * Set the TWI transaction bitrate - */ -void i2cSetBitrate(void) -{ - /* For processors with additional bitrate division, we set the prescaler */ -#ifdef TWPS0 - TWSR = I2C_TWSR; -#endif +//i2c_exit(); +//i2c_master_send(); +//i2c_master_recv(); +//static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length); + //i2c_suspend(); + //i2c_resume(); - /* Setting TWBR. There's a note in the datasheet that TWBR can't be lower - * than 10 if the TWI is operated in Master mode */ -#if ((F_CPU/1000UL)/I2C_SCL_FREQ_KHZ) < 36 - TWBR = 10; /* smallest TWBR value */ -#else - TWBR = I2C_TWBR; -#endif -} - -/* - * This function initializes the TWI interface for i2c communication. You - * should use the definitions in i2c.h to select the mode you want. - * - * Note that you should set the i2cSlaveReceive and i2cSlaveTransmit handlers - * after this initialization otherwise they'll be cleared. - */ -void i2cInit(void) -{ - /* Set bitrate */ - i2cSetBitrate(); - - /* Set pull-up resistors on I2C bus pins */ -#ifdef I2C_PULLUP - PORTC |= _BV(5); /* i2c SCL on ATmega48,88,168 */ - PORTC |= _BV(4); /* i2c SDA on ATmega48,88,168 */ -#endif - -#ifdef TWI_SLA_ENABLED /* If Slave mode: */ - /* Set local device address (used in slave mode only) */ - TWAR = I2C_TWAR; - TWCR = I2C_MODE; /* enable TWI */ -#else -#ifdef TWI_M_ENABLED - TWCR = I2C_MODE; /* enable TWI */ -#endif -#endif -} - /* Set the user function which handles receiving (incoming) data as a slave */ void -i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func) +i2c_master_receive_handler(void (*i2cMasterRx_func) (uint8_t receiveDataLength, uint8_t * recieveData)) { - i2cSlaveReceive = i2cSlaveRx_func; + i2c_master_receive = i2cMasterRx_func; } /* Set the user function which handles transmitting (outgoing) data as a slave */ @@ -146,288 +116,320 @@ i2cSlaveTransmit = i2cSlaveTx_func; } -static inline void i2cSendStart(void); - -/* - * Start a Master transmission - */ -void i2cMasterStart(void) +static inline void twi_reset(void) { - volatile uint8_t wait; - - for (wait = 0; wait < 20; wait++) ; /* add a delay for slaves to have time to process data when they receive a stop in case the main loop is too short so that a stop can be immediately followed by a start */ - /* note: when a stop is immediately followed by a start, the slave detects - * the stop, need to process the received data and exit the ISR. If it - * doesn't have enough time to do this before a new start is set, it will - * still be in ISR when the start occurs and won't detect it because the - * TWI hardware is said to be partly disabled when TWINT is set. - * Two things to do here, exit the ISR as soon as possible in the slave, - * and add some delay between a stop and a start in the master. Use a - * repeated start condition in such a case. */ - if (i2cFlags.i2c_busy == 0) // if a start is sent when the TWI is receiving, one of the data will be received as 0x78 instead of 0x80 so we get a corruption there - i2cSendStart(); -#if (__i2c_signals__) - BUSY_PT |= BUSY_MK; /* 'BUSY' debug signal */ - START_PT |= START_MK; /* 'START' debug signal */ -#endif -} - -static inline void i2cReset(void) -{ #if (__i2c_debug__) - i2cStatusIdx %= 32; - i2cStatus[i2cStatusIdx++] = 0x02; + twi_debug_stack_idx %= I2C_DBG_STACK_SIZE; + twi_debug_stack[twi_debug_stack_idx++] = 0x02; #endif - i2cFlags.i2c_busy = 0; /* XXX check if this flag is reset in all possible conditions */ #if (__i2c_signals__) BUSY_PT &= ~BUSY_MK; /* 'BUSY' debug signal */ #endif - // TWCR = I2C_MODE; - TWCR |= I2C_MODE; + /* In case we lost arbitration, we still want to send the start later on so + * we should 'OR' here and not simply assign. */ + TWCR |= TWI_TWCR; } -static inline void i2cSendStart(void) +/** Send a START or repeated START condition on the bus. + * If a STOP condition just occurred on the bus, a START will be sent. + * Otherwise a repeated START will be sent. */ +static inline void twi_send_start(void) { #if (__i2c_debug__) - cli(); /* we need to protect this from an i2c interrupt that will corrupt those values otherwise */ - i2cStatusIdx %= 32; - i2cStatus[i2cStatusIdx++] = 0x03; + cli(); /* we need to protect this from an i2c interrupt that will corrupt + those values otherwise */ + twi_debug_stack_idx %= I2C_DBG_STACK_SIZE; + twi_debug_stack[twi_debug_stack_idx++] = 0x03; sei(); #endif - // TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWSTA); - TWCR = _BV(TWSTA) | I2C_MODE; + TWCR = _BV(TWSTA) | TWI_TWCR; } -static inline void i2cSendStop(void) +static inline void twi_send_stop(void) { #if (__i2c_debug__) - i2cStatusIdx %= 32; - i2cStatus[i2cStatusIdx++] = 0x04; + twi_debug_stack_idx %= I2C_DBG_STACK_SIZE; + twi_debug_stack[twi_debug_stack_idx++] = 0x04; #endif - TWCR = _BV(TWSTO) | I2C_MODE; - i2cFlags.i2c_busy = 0; + TWCR = _BV(TWSTO) | TWI_TWCR; #if (__i2c_signals__) BUSY_PT &= ~BUSY_MK; /* 'BUSY' debug signal */ #endif } -static inline void i2cSendByte(uint8_t data) +static inline void twi_send_data(uint8_t data) { #if (__i2c_debug__) - i2cStatusIdx %= 32; - i2cStatus[i2cStatusIdx++] = 0x05; + twi_debug_stack_idx %= I2C_DBG_STACK_SIZE; + twi_debug_stack[twi_debug_stack_idx++] = 0x05; #endif TWDR = data; - TWCR = I2C_MODE; + /* Just clear the interrupt flag. */ + TWCR = TWI_TWCR; } -static inline void i2cAckReceiveByte(void) +static inline uint8_t twi_get_data(void) { - TWCR |= _BV(TWEA); + return TWDR; } -static inline void i2cNackReceiveByte(void) +static inline void twi_return_ack(void) { - TWCR &= ~_BV(TWEA); + TWCR = TWI_TWCR | _BV(TWEA); } -static inline uint8_t i2cGetReceivedByte(void) +static inline void twi_return_nack(void) { - return TWDR; + TWCR = TWI_TWCR & ~_BV(TWEA); } -static inline uint8_t i2cGetStatus(void) +static inline uint8_t twi_get_status(void) { return TWSR; } -/* TWI interrupt service routine */ -#ifdef TWI_INT_ENABLED -ISR(SIG_TWI) +static inline uint8_t twi_poll_status(void) { - const static void *twiLookupTable[] = { - &&case_TW_BUS_ERROR, - &&case_TW_START, - &&case_TW_REP_START, - &&case_TW_MT_SLA_ACK, - &&case_TW_MT_SLA_NACK, - &&case_TW_MT_DATA_ACK, - &&case_TW_MT_DATA_NACK, - &&case_TW_MT_ARB_LOST, - &&case_TW_MR_SLA_ACK, - &&case_TW_MR_SLA_NACK, - &&case_TW_MR_DATA_ACK, - &&case_TW_MR_DATA_NACK, - &&case_TW_SR_SLA_ACK, - &&case_TW_SR_ARB_LOST_SLA_ACK, - &&case_TW_SR_GCALL_ACK, - &&case_TW_SR_ARB_LOST_GCALL_ACK, - &&case_TW_SR_DATA_ACK, - &&case_TW_SR_DATA_NACK, - &&case_TW_SR_GCALL_DATA_ACK, - &&case_TW_SR_GCALL_DATA_NACK, - &&case_TW_SR_STOP, - &&case_TW_ST_SLA_ACK, - &&case_TW_ST_ARB_LOST_SLA_ACK, - &&case_TW_ST_DATA_ACK, - &&case_TW_ST_DATA_NACK, - &&case_TW_ST_LAST_DATA, - &&case_, - &&case_, - &&case_, - &&case_, - &&case_, - &&case_TW_NO_INFO, - }; + while (!(TWCR & _BV(TWINT))); ... [truncated message content] |