From bfabdf3ed83f75bd36be16e23dda54b5d1e38520 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Mon, 29 Nov 2010 23:08:04 +0100 Subject: digital/dev2: add TWI master module --- digital/dev2/src/common/usb_twi.c | 279 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 digital/dev2/src/common/usb_twi.c (limited to 'digital/dev2/src/common/usb_twi.c') diff --git a/digital/dev2/src/common/usb_twi.c b/digital/dev2/src/common/usb_twi.c new file mode 100644 index 00000000..e83a94d9 --- /dev/null +++ b/digital/dev2/src/common/usb_twi.c @@ -0,0 +1,279 @@ +/* usb_twi.c */ +/* dev2 - Multi-purpose development board using USB and Ethernet. {{{ + * + * Copyright (C) 2010 Nicolas Schodet + * + * APBTeam: + * Web: http://apbteam.org/ + * Email: team AT apbteam DOT org + * + * 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. + * + * }}} */ +#include "common.h" + +#include "usb_twi.h" +#include "descriptors.h" + +#include "modules/twi/twi.h" +#include "modules/usb/usb.h" + +/** Parser steps. */ +enum +{ + /** Nothing received yet. */ + PARSER_WAIT_COMMAND, + /** Command received, wait address most significant quartet. */ + PARSER_WAIT_ADDRESS_MSQ, + /** First address quartet received, wait second one. */ + PARSER_WAIT_ADDRESS_LSQ, + /** Wait data or end of line. */ + PARSER_WAIT_DATA_OR_EOL, + /** Wait data second quartet. */ + PARSER_WAIT_DATA_LSQ, +}; + +/** Context. */ +struct usb_twi_t +{ + /** Buffer used for transmission or reception. */ + uint8_t buffer[TWI_BUFFER_SIZE]; + /** Number of bytes in buffer. */ + uint8_t buffer_tail; + /** Number of bytes sent back to USB. */ + uint8_t buffer_head; + /** Command to execute. */ + char command; + /** Slave address. */ + uint8_t slave; + /** Parser step. */ + uint8_t parser_step; + /** Parser first quartet. */ + uint8_t parser_msq; + /** Output buffer (in case of USB not ready). */ + char output_buffer[4]; + /** Position in output buffer of next character to send. */ + uint8_t output_buffer_head; +}; + +/** Global context. */ +struct usb_twi_t usb_twi_global; +#define ctx usb_twi_global + +void +usb_twi_init (void) +{ + twi_init (0xfe); +} + +void +usb_twi_uninit (void) +{ + twi_uninit (); +} + +/** Return non zero if there is characters to send. */ +static uint8_t +usb_twi_poll (void) +{ + return ctx.output_buffer[ctx.output_buffer_head]; +} + +/** Output byte to output buffer. */ +static void +usb_twi_ouput_byte (uint8_t p, uint8_t b) +{ + uint8_t i; + char c; + for (i = 0; i < 2; i++) + { + c = "0123456789abcdef"[b >> 4]; + ctx.output_buffer[p++] = c; + b <<= 4; + } +} + +/** Return a character from the output buffer and reload it from buffer if + * needed. */ +static char +usb_twi_getc (void) +{ + char c; + uint8_t p; + /* Take a character in output buffer. */ + c = ctx.output_buffer[ctx.output_buffer_head]; + ctx.output_buffer_head++; + /* Reload from buffer if needed. */ + if (!ctx.output_buffer[ctx.output_buffer_head] + && ctx.buffer_head != ctx.buffer_tail) + { + ctx.output_buffer_head = 0; + p = 0; + usb_twi_ouput_byte (p, ctx.buffer[ctx.buffer_head++]); + p += 2; + if (ctx.buffer_head == ctx.buffer_tail) + ctx.output_buffer[p++] = '\r'; + ctx.output_buffer[p] = '\0'; + } + return c; +} + +static uint8_t +usb_twi_get_hex (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + return 0xff; +} + +static uint8_t +usb_twi_exec (void) +{ + uint8_t r, p, l; + if (ctx.command == 's') + { + twi_master_send (ctx.slave, ctx.buffer, ctx.buffer_tail); + r = twi_master_wait (); + ctx.buffer[0] = r; + ctx.buffer_head = 0; + ctx.buffer_tail = 1; + ctx.output_buffer_head = 0; + p = 0; + ctx.output_buffer[p++] = 'S'; + ctx.output_buffer[p++] = '\0'; + return 1; + } + else if (ctx.command == 'r' && ctx.buffer_tail == 1) + { + l = ctx.buffer[0]; + twi_master_recv (ctx.slave, ctx.buffer, l); + r = twi_master_wait (); + ctx.buffer_head = 0; + ctx.buffer_tail = r; + ctx.output_buffer_head = 0; + p = 0; + ctx.output_buffer[p++] = 'R'; + if (r == 0) + ctx.output_buffer[p++] = '\r'; + ctx.output_buffer[p++] = '\0'; + return 1; + } + else + return 0; +} + +static void +usb_twi_accept (char c) +{ + uint8_t q; + /* Purge output buffer. */ + ctx.output_buffer_head = 0; + ctx.output_buffer[0] = '\0'; + /* Accept FSM. */ + switch (ctx.parser_step) + { + case PARSER_WAIT_COMMAND: + if (c == 's' || c == 'r') + { + ctx.command = c; + ctx.buffer_tail = 0; + ctx.parser_step++; + return; + } + break; + case PARSER_WAIT_ADDRESS_MSQ: + q = usb_twi_get_hex (c); + if (q != 0xff) + { + ctx.parser_msq = q; + ctx.parser_step++; + return; + } + break; + case PARSER_WAIT_ADDRESS_LSQ: + q = usb_twi_get_hex (c); + if (q != 0xff) + { + ctx.slave = ctx.parser_msq << 4 | q; + ctx.parser_step++; + return; + } + break; + case PARSER_WAIT_DATA_OR_EOL: + if (c == '\r') + { + ctx.parser_step = PARSER_WAIT_COMMAND; + if (usb_twi_exec ()) + return; + } + else if (ctx.buffer_tail < TWI_BUFFER_SIZE) + { + q = usb_twi_get_hex (c); + if (q != 0xff) + { + ctx.parser_msq = q; + ctx.parser_step++; + return; + } + } + break; + case PARSER_WAIT_DATA_LSQ: + q = usb_twi_get_hex (c); + if (q != 0xff) + { + ctx.buffer[ctx.buffer_tail++] = ctx.parser_msq << 4 | q; + ctx.parser_step--; + return; + } + break; + } + /* If here, there was an error. */ + ctx.buffer_tail = ctx.buffer_head = 0; + ctx.output_buffer_head = 0; + ctx.output_buffer[0] = '!'; + ctx.output_buffer[1] = '\r'; + ctx.output_buffer[2] = '\0'; + ctx.parser_step = PARSER_WAIT_COMMAND; +} + +void +usb_twi_task (void) +{ + Endpoint_SelectEndpoint (TWI_RX_EPNUM); + /* If data is available from USB: */ + if (Endpoint_ReadWriteAllowed ()) + { + /* Read as much as possible, and clear endpoint. */ + do { + usb_twi_accept (Endpoint_Read_Byte ()); + } while (Endpoint_ReadWriteAllowed ()); + Endpoint_ClearCurrentBank (); + } + /* If data is available from uart and there is room in the TX endpoint: */ + Endpoint_SelectEndpoint (TWI_TX_EPNUM); + if (usb_twi_poll () && Endpoint_ReadWriteAllowed ()) + { + do { + Endpoint_Write_Byte (usb_twi_getc ()); + } while (usb_twi_poll () && Endpoint_ReadWriteAllowed ()); + Endpoint_ClearCurrentBank (); + } +} + -- cgit v1.2.3