diff --git a/.gitignore b/.gitignore index c6127b3..6dcae4b 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,7 @@ *.app *.i*86 *.x86_64 -*.hex +#*.hex # Debug files *.dSYM/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b6534be --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +MAIN = ELM327SLCAN +SRC = frontend.c init.c rbuf.c clock.c main.c can.c +CC = C:\Program Files\Microchip\xc8\v1.42\bin\xc8.exe +CHIP = 18F25K80 + +all: $(MAIN).hex + +$(MAIN).hex: $(SRC) + $(CC) $(SRC) --chip=$(CHIP) --MODE=pro --OPT=+speed --OUTDIR=out -O$(MAIN) --ROM=default,-7cfc-7fff + +clean: + rm -f $(MAIN).hex funclist $(MAIN).cof $(MAIN).hxl $(MAIN).p1 $(MAIN).sdb startup.* $(MAIN).lst $(MAIN).pre $(MAIN).sym + diff --git a/README.md b/README.md new file mode 100644 index 0000000..32f994f --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +#### ELM327SLCAN + +The cheap vehicle's CAN bus sniffer based on ELM327, special [bootloader](https://fischl.de/usbtin/), and [USBtin](https://fischl.de/usbtin/). +With this device you can monitor vehicle's CAN bus. Monitoring software – [CANHacker](https://cdn.hackaday.io/files/12644540960896/CANHackerV2.00.02.zip) or [similar](https://fischl.de/usbtin/#usbtinviewer). + +##### LEDs + +1. Red – power supply. +2. Green – current connection status. If lights steady – connection is open. Flashes once in three seconds – connection is closed. +3. Orange – error status. + - If lights steady – serial port buffer overflow error. This error occurs when the MCU does not have time to read the next command from PC. You can reset this error only by turning off the power supply. + - One flash in three seconds – CAN messages FIFO-buffer overflow in MCU. The depth of a FIFO-buffer is eight messages. Then the ninth message is received, this error is raised. + - Double flash in three seconds – internal text buffer overflow in MCU. This error is raised then the rate of incoming CAN-messages is very high and MCU does not have time to send them to PC. + - Triple flash in three seconds – internal text buffer overflow in MCU. This error is raised then the rate of incoming commands from PC is very high and MCU does not have time to process them all. + + The last three errors could be reset with f2 command. + + +##### Switch to Bootloader mode for firmware update: + +1. Issue the command B10[CR] in RS232 terminal software (for instance, [termite](https://www.compuphase.com/software_termite.htm)). +2. Disconnect the COM port in RS232 terminal software. +3. Press the red button 'Bootloader Mode' in [Serial Bootloader AN1310](http://ww1.microchip.com/downloads/en/AppNotes/Serial%20Bootloader%20AN1310%20v1.05r.zip) software. +4. Switching to Bootloader mode is reflected in the status bar. +5. Update the firmware. +6. To start the updated application it is need to turn off and on the power. + + +![](images/ELM327SLCAN.png) + + +##### Filtering + +Implemented filtering 'Single filter mode' with three modifications as described in [SJA1000](https://www.nxp.com/docs/en/data-sheet/SJA1000.pdf) datasheet and [AN97076](https://www.nxp.com/docs/en/application-note/AN97076.pdf). The differences: + +1. RTR bit does not take part in filtering. +2. Two message's bytes do not take part in filtering. LSB ACR3 must be zero in case of work in CAN bus with 11-bit IDs. +3. LSB ACR3 is copied to EXIDEN bit of RXF0SIDL. LSB AMR3 is copied to EXIDEN bit of RXM0SIDL. See the PIC18FXXK80 datasheet. One mask (RXM0) and one filter (RXF0) are used. + +To activate the CANHacker Filter settings it is necessary that the connection has been already established (Connected to …). So - see the first Termite window: + +1. Establish connection. +2. Click Filter button. +3. Set Mask Filter in the upper group box. +4. Click OK button. As a result, we get ‘close’, ‘set mask’, ‘set filter’, ‘open’ commands. + +Range Filter filtering (lower group box) works in software. Issued command allows all incoming messages (mFFFFFF). + +If you remove both checkboxes and press OK button, issued command allows all incoming messages (mFFFFFF). See the second Termite window. + + +![](images/filtering.png) + +##### Bluetooth + +Bluetooth name: OBDII +Bluetooth PIN: 1234 +Serial port settings: 38400, 8n1 + + +##### To build new firmware, it is need to install Microchip XC8 Compiler and MinGW. + +![](images/cmd.png) diff --git a/can.c b/can.c new file mode 100644 index 0000000..091337c --- /dev/null +++ b/can.c @@ -0,0 +1,230 @@ +#include +#include "elm327slcan.h" +#include "clock.h" +#include "can.h" + +/** + * \brief Write to given register + * + * \param address Register address + * \param data Value to write to given register + */ +void can_write_register(unsigned short address, unsigned char data) { + FSR0 = address; + INDF0 = data; +} + +/** + * \brief Read from given register + * + * \param address Register address + * \return register value + */ +unsigned char can_read_register(unsigned short address) { + FSR0 = address; + + return INDF0; +} + +/** + * \brief Set filter mask of given SJA1000 register values + * + * \param amr0 Acceptence mask register 0 + * \param amr1 Acceptence mask register 1 + * \param amr2 Acceptence mask register 2 + * \param amr3 Acceptence mask register 3 + * + * This function has only affect if can controller is in configuration mode. + */ +void can_set_SJA1000_filter_mask(unsigned char amr0, unsigned char amr1, unsigned char amr2, unsigned char amr3) { + + // SJA1000 mask bit definition: 1 = accept without matching, 0 = do matching with acceptance code + // Microchip mask bit definition: 0 = accept without matching, 1 = do matching with acceptance filter + // -> invert mask + + RXM0SIDH = ~amr0; + RXM0SIDL = ((~amr1) & 0xE0) | (((~amr1) >> 3) & 3); + if (amr3 & 1) RXM0SIDL |= 8; + RXM0EIDH = ((~amr2) >> 3) | ((~amr1) << 5); + RXM0EIDL = ((~amr3) >> 3) | ((~amr2) << 5); +/* + // mask for filter 1 + RXM0SIDH = ~amr0; + RXM0SIDL = ((~amr1) & 0xE3) | 8; + RXM0EIDH = ~amr2; + RXM0EIDL = ~amr3; + + // mask for filter 2 + RXM1SIDH = ~amr2; + RXM1SIDL = (~amr3) & 0xE0; + RXM1EIDH = 0x00; + RXM1EIDL = 0x00; +*/ +} + +/** + * \brief Set filter code of given SJA1000 register values + * + * \param amr0 Acceptence code register 0 + * \param amr1 Acceptence code register 1 + * \param amr2 Acceptence code register 2 + * \param amr3 Acceptence code register 3 + * + * This function has only affect if controller is in configuration mode. + */ +void can_set_SJA1000_filter_code(unsigned char acr0, unsigned char acr1, unsigned char acr2, unsigned char acr3) { + RXF0SIDH = acr0; + RXF0SIDL = (acr1 & 0xE0) | ((acr1 >> 3) & 3); + if (acr3 & 1) RXF0SIDL |= 8; + RXF0EIDH = (acr2 >> 3) | (acr1 << 5); + RXF0EIDL = (acr3 >> 3) | (acr2 << 5); +/* + // acceptance code for filter 1 + RXF0SIDH = acr0; + RXF0SIDL = (acr1) & 0xE0; // standard + RXF1SIDH = acr0; + RXF1SIDL = ((acr1) & 0xE0) | 0x08; // extended + + // acceptance code for filter 2 + RXF2SIDH = acr2; + RXF2SIDL = (acr3) & 0xE0; // standard + RXF3SIDH = acr2; + RXF3SIDL = ((acr3) & 0xE0) | 0x08; // extended + + // fill remaining filters with zero +// RXF4SIDH = 0x00; +// RXF4SIDL = 0x00; +// RXF5SIDH = 0x00; +// RXF5SIDL = 0x00; +*/ +} + +/** + * \brief Set bit timing registers + * + * \param cnf1 Configuration register 1 + * \param cnf2 Configuration register 2 + * \param cnf3 Configuration register 3 + * + * This function has only affect if controller is in configuration mode + */ +void can_set_bittiming(unsigned char cnf1, unsigned char cnf2, unsigned char cnf3) { + + BRGCON1 = cnf1; + BRGCON2 = cnf2; + BRGCON3 = cnf3; +} + +/** + * \brief Send given CAN message + * + * \ p_canmsg Pointer to can message to send + * \return 1 if transmitted successfully to transmit buffer, 0 on error (= no free buffer available) + */ +unsigned char can_send_message(canmsg_t * p_canmsg) { + ECANCON &= 0xE0; + ECANCON |= 0x05; + + if (RXB0CON & 0x08) { + ECANCON--; + if (RXB0CON & 0x08) { + ECANCON--; + if (RXB0CON & 0x08) { + return 0; //All TX buffers are busy + } + } + } + + unsigned char length = p_canmsg->dlc; + if (length > 8) length = 8; + + if (p_canmsg->flags.extended) { + RXB0SIDH = p_canmsg->id >> 21; + RXB0SIDL = ((p_canmsg->id >> 13) & 0xe0) | ((p_canmsg->id >> 16) & 0x03) | 0x08; + RXB0EIDH = p_canmsg->id >> 8; + RXB0EIDL = p_canmsg->id; + } else { + RXB0SIDH = p_canmsg->id >> 3; + RXB0SIDL = p_canmsg->id << 5; + } + + RXB0DLC = length; + + if (p_canmsg->flags.rtr) { + RXB0DLC = length | 0x40; + } else { + if (length) { + unsigned char *pTxBuf, i; + + pTxBuf = &RXB0D0; + for (i = 0; i < length; i++) { + *pTxBuf++ = p_canmsg->data[i]; + } + } + } + + RXB0CON |= 8; + + return 1; +} + +/* + * \brief Read out one can message from controller + * + * \param p_canmsg Pointer to can message structure to fill + * \return 1 on success, 0 if there is no message to read + */ +unsigned char can_receive_message(canmsg_t * p_canmsg) { + + //unsigned char address; + + if (nFIFOEMPTY == 0) return 0; + + // store timestamp + p_canmsg->timestamp = clock_getMS(); + + ECANCON &= 0xE0; + ECANCON |= ((CANCON & 0x07) | 0x10); + + if (RXB0FUL == 0) return 0; + + unsigned char sidh = RXB0SIDH; + unsigned char sidl = RXB0SIDL; + + if (sidl & 0x08) { + // extended + p_canmsg->flags.extended = 1; + p_canmsg->id = (unsigned long) sidh << 21; + p_canmsg->id |= (unsigned long)(sidl & 0xe0) << 13; + p_canmsg->id |= (unsigned long)(sidl & 0x03) << 16; + p_canmsg->id |= (unsigned long)(RXB0EIDH) << 8; + p_canmsg->id |= (unsigned long) RXB0EIDL; + unsigned char dlc = RXB0DLC; + p_canmsg->dlc = dlc & 0x0f; + p_canmsg->flags.rtr = (dlc >> 6) & 0x01; + } else { + // standard + p_canmsg->flags.extended = 0; + p_canmsg->flags.rtr = (sidl >> 4) & 0x01; + p_canmsg->id = (unsigned long) sidh << 3; + p_canmsg->id |= (unsigned long) sidl >> 5; + p_canmsg->dlc = RXB0DLC & 0x0f; + } + + // get data + if (!p_canmsg->flags.rtr) { + unsigned char i, *ptr; + unsigned char length = p_canmsg->dlc; + if (length > 8) length = 8; + ptr = &RXB0D0; + for (i = 0; i < length; i++) { + p_canmsg->data[i] = *ptr++; + } + } + + RXB0FUL = 0; + NOP(); + RXBnIF = 0; + + return 1; +} diff --git a/can.h b/can.h new file mode 100644 index 0000000..864a481 --- /dev/null +++ b/can.h @@ -0,0 +1,41 @@ +#ifndef _CAN_H_INC +#define _CAN_H_INC + +// timing for Fosc = 16MHz // KVN +#define CAN_TIMINGS_10K 0x00 | 49, 0x80 | ((4-1)<<3) | (7-1), 4-1 // Prop=7, PS1=4, PS2=4, SamplePoint=75% +#define CAN_TIMINGS_20K 0x00 | 39, 0x80 | ((2-1)<<3) | (5-1), 2-1 // Prop=5, PS1=2, PS2=2, SamplePoint=80% +#define CAN_TIMINGS_50K 0x00 | 15, 0x80 | ((2-1)<<3) | (5-1), 2-1 // Prop=5, PS1=2, PS2=2, SamplePoint=80% +#define CAN_TIMINGS_100K 0x00 | 07, 0x80 | ((2-1)<<3) | (5-1), 2-1 // Prop=5, PS1=2, PS2=2, SamplePoint=80% +#define CAN_TIMINGS_125K 0x00 | 07, 0x80 | ((2-1)<<3) | (3-1), 2-1 // Prop=3, PS1=2, PS2=2, SamplePoint=75% +#define CAN_TIMINGS_250K 0x00 | 03, 0x80 | ((2-1)<<3) | (3-1), 2-1 // Prop=3, PS1=2, PS2=2, SamplePoint=75% +#define CAN_TIMINGS_500K 0x00 | 01, 0x80 | ((2-1)<<3) | (3-1), 2-1 // Prop=3, PS1=2, PS2=2, SamplePoint=75% +#define CAN_TIMINGS_800K 0x00 | 00, 0x80 | ((2-1)<<3) | (5-1), 2-1 // Prop=5, PS1=2, PS2=2, SamplePoint=80% +#define CAN_TIMINGS_1M 0x00 | 00, 0x80 | ((2-1)<<3) | (3-1), 2-1 // Prop=3, PS1=2, PS2=2, SamplePoint=75% + +// can message data structure +typedef struct +{ + unsigned long id; // identifier (11 or 29 bit) + struct { + unsigned char rtr : 1; // remote transmit request + unsigned char extended : 1; // extended identifier + } flags; + + unsigned char dlc; // data length code + unsigned char data[8]; // payload data + unsigned short timestamp; // timestamp +} canmsg_t; + +// function prototypes +//extern unsigned char can_init(); +extern unsigned char can_read_register(unsigned short); +extern void can_write_register(unsigned short, unsigned char); +//extern void can_bit_modify(unsigned char address, unsigned char mask, unsigned char data); +extern void can_set_SJA1000_filter_mask(unsigned char, unsigned char, unsigned char, unsigned char); +extern void can_set_SJA1000_filter_code(unsigned char, unsigned char, unsigned char, unsigned char); +extern unsigned char can_read_errorflags(void); +extern void can_set_bittiming(unsigned char, unsigned char, unsigned char); +extern unsigned char can_send_message(canmsg_t *); +extern unsigned char can_receive_message(canmsg_t *); + +#endif diff --git a/clock.c b/clock.c new file mode 100644 index 0000000..3a011a5 --- /dev/null +++ b/clock.c @@ -0,0 +1,48 @@ +#include +#include +#include "clock.h" +#include "elm327slcan.h" + +extern uint8_t state; + +static volatile uint16_t clock_msticker = 0, tmp; +static volatile uint8_t led_ticker = 0; + +void __interrupt(low_priority) myLoIsr(void) { + if (CCP1IF && CCP1IE) { + CCP1IF = 0; + // 1 ms + if (++clock_msticker == 60000) clock_msticker = 0; + } + + if (CCP2IF && CCP2IE) { + CCP2IF = 0; + // 100 ms + led_ticker = (led_ticker + 1) & 31; + stateLED((led_ticker == 0) || (state != STATE_CONFIG)); + + if (OERR()) { // steady orange light + errorLED(1); // device must be repowered + } else if (RXBnOVFL()) { // one orange light flash + errorLED((led_ticker & 2) && (led_ticker < 4)); + } else if (RX_OVFL()) { // two orange light flash + errorLED((led_ticker & 2) && (led_ticker < 8)); + } else if (TX_OVFL()) { // three orange light flash + errorLED((led_ticker & 2) && (led_ticker < 12)); + } + } +} + +uint16_t clock_getMS(void) { + CCP1IE = 0; + tmp = clock_msticker; + CCP1IE = 1; + + return tmp; +} + +void clock_reset(void) { + CCP1IE = 0; + clock_msticker = 0; + CCP1IE = 1; +} diff --git a/clock.h b/clock.h new file mode 100644 index 0000000..9153456 --- /dev/null +++ b/clock.h @@ -0,0 +1,12 @@ +#ifndef _CLOCK_ +#define _CLOCK_ + +#include + +extern uint16_t clock_getMS(void); +extern void clock_reset(void); + +//#define CLOCK_TIMERTICKS_1MS 500 +//#define CLOCK_TIMERTICKS_100MS 50000 + +#endif diff --git a/elm327slcan.h b/elm327slcan.h new file mode 100644 index 0000000..0edd0f3 --- /dev/null +++ b/elm327slcan.h @@ -0,0 +1,69 @@ +#ifndef __CONFIG_H_INC +#define __CONFIG_H_INC + +#include + +#define VERSION_HARDWARE_MAJOR 1 +#define VERSION_HARDWARE_MINOR 0 +#define VERSION_FIRMWARE_MAJOR 1 +#define VERSION_FIRMWARE_MINOR 8 + +#define BOOTLOADER_ENTRY_ADDRESS 0x7D00 + +#define STATE_CONFIG 0 +#define STATE_OPEN 1 +#define STATE_LISTEN 2 + +#define _XTAL_FREQ 16000000UL +#define BAUDRATE 38400UL +#define BRGVALUE ((_XTAL_FREQ + 2*BAUDRATE) / (4*BAUDRATE) - 1) + +#if defined(__DEBUG) + #define UxSPBRG SPBRG2 + #define UxSPBRGH SPBRGH2 + #define UxRCSTA RCSTA2 + #define UxTXSTA TXSTA2 + #define UxRCREG RCREG2 + #define UxTXREG TXREG2 + #define UxPIR PIR3 + #define UxRCIF PIR3bits.RC2IF + #define UxTXIF PIR3bits.TX2IF + #define UxRCIE PIE3bits.RC2IE + #define UxTXIE PIE3bits.TX2IE + #define UxBAUDCON BAUDCON2 + #define OERR() OERR2 + + #define stateLED(value) LATA0 = (value) + #define errorLED(error) LATA5 = (error) +#else + #define UxSPBRG SPBRG1 + #define UxSPBRGH SPBRGH1 + #define UxRCSTA RCSTA1 + #define UxTXSTA TXSTA1 + #define UxRCREG RCREG1 + #define UxTXREG TXREG1 + #define UxPIR PIR1 + #define UxRCIF PIR1bits.RC1IF + #define UxTXIF PIR1bits.TX1IF + #define UxRCIE PIE1bits.RC1IE + #define UxTXIE PIE1bits.TX1IE + #define UxBAUDCON BAUDCON1 + #define OERR() OERR1 + + #define stateLED(value) LATB7 = !(value) + #define errorLED(error) LATB6 = !(error) +#endif + +#define RXBnOVFL() RXBNOVFL +#define RX_OVFL() LATA2 +#define TX_OVFL() LATA3 + +#define SET_RX_OVFL() LATA2 = 1 +#define SET_TX_OVFL() LATA3 = 1 +#define RESET_RX_OVFL() LATA2 = 0 +#define RESET_TX_OVFL() LATA3 = 0 + +#define cmdSend() UxTXIE = 1 +#define cmdStop() UxTXIE = 0 + +#endif diff --git a/frontend.c b/frontend.c new file mode 100644 index 0000000..f8449ac --- /dev/null +++ b/frontend.c @@ -0,0 +1,517 @@ +#include +#include +#include "can.h" +#include "clock.h" +#include "elm327slcan.h" +#include "frontend.h" +#include "rbuf.h" + +extern uint8_t state; +extern rbuf_t sRing; + +#define cmdPush(data) do { \ + if (!rbuf_push((rbuf_t *)&sRing, data)) \ + SET_RX_OVFL(); \ + } while (0) + +unsigned char timestamping = 0; + +/** + * Parse hex value of given string + * + * @param line Input string + * @param len Count of characters to interpret + * @param value Pointer to variable for the resulting decoded value + * @return 0 on error, 1 on success + */ +unsigned char parseHex(char * line, unsigned char len, unsigned long * value) { + *value = 0; + while (len--) { + if (*line == 0) return 0; + *value <<= 4; + if ((*line >= '0') && (*line <= '9')) { + *value += *line - '0'; + } else if ((*line >= 'A') && (*line <= 'F')) { + *value += *line - 'A' + 10; + } else if ((*line >= 'a') && (*line <= 'f')) { + *value += *line - 'a' + 10; + } else return 0; + line++; + } + return 1; +} + +/** + * Send given byte value as hexadecimal string + * + * @param value Byte value to send over UART + */ +void sendByteHex(unsigned char value) { + +// sendHex(value, 2); + + unsigned char ch = value >> 4; + if (ch > 9) ch = ch - 10 + 'A'; + else ch = ch + '0'; + cmdPush(ch); + + ch = value & 0xF; + if (ch > 9) ch = ch - 10 + 'A'; + else ch = ch + '0'; + cmdPush(ch); + +} + +/** + * Interprets given line and transmit can message + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_transmit(char *line) { + canmsg_t canmsg; + unsigned long temp; + unsigned char idlen; + + canmsg.flags.rtr = ((line[0] == 'r') || (line[0] == 'R')); + + // upper case -> extended identifier + if (line[0] < 'Z') { + canmsg.flags.extended = 1; + idlen = 8; + } else { + canmsg.flags.extended = 0; + idlen = 3; + } + + if (!parseHex(&line[1], idlen, &temp)) return 0; + canmsg.id = temp; + + if (!parseHex(&line[1 + idlen], 1, &temp)) return 0; + canmsg.dlc = temp; + + if (!canmsg.flags.rtr) { + unsigned char i; + unsigned char length = canmsg.dlc; + if (length > 8) length = 8; + for (i = 0; i < length; i++) { + if (!parseHex(&line[idlen + 2 + i*2], 2, &temp)) return 0; + canmsg.data[i] = temp; + } + } + + return can_send_message(&canmsg); +} + +/** + * Interprets given line and set up bit timing + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_setupUserdefined(char * line) { + + if (state == STATE_CONFIG) { + unsigned long cnf1, cnf2, cnf3; + if (parseHex(&line[1], 2, &cnf1) && parseHex(&line[3], 2, &cnf2) && parseHex(&line[5], 2, &cnf3)) { + can_set_bittiming(cnf1, cnf2, cnf3); + return CR; + } + } + return BELL; +} + +/** + * Interprets given line and reads register + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_readRegister(char * line) { + + unsigned long address; + if (parseHex(&line[1], 3, &address)) { + unsigned char value = can_read_register(address); + sendByteHex(value); + return CR; + } + return BELL; +} + +/** + * Interprets given line and writes register + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_writeRegister(char * line) { + + unsigned long address, data; + if (parseHex(&line[1], 3, &address) && parseHex(&line[4], 2, &data)) { + can_write_register(address, data); + return CR; + } + return BELL; +} + +/** + * Interprets given line and set time stamping + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_setTimestamping(char * line) { + + unsigned long stamping; + if (parseHex(&line[1], 1, &stamping)) { + timestamping = (stamping != 0); + return CR; + } + + return BELL; +} + +/** + * Send out given error flags + * + * @param flags Error flags to send out + */ +void frontend_sendErrorflags(unsigned char flags) { + + cmdPush('F'); + sendByteHex(flags); + cmdPush(CR); +} + +/** + * Interprets given line and handle status flag requests + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_errorFlags(char * line) { + unsigned char flags = COMSTAT & 0x7F; + + cmdPush('F'); + sendByteHex(flags); + return CR; +} + +/** + * Interprets given line and handle error reporting requests + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_errorReporting(char * line) { + + unsigned long subcmd = 0; + + if (parseHex(&line[1], 1, &subcmd)) { + if (subcmd == 2) { + RXBNOVFL = 0; + RESET_RX_OVFL(); + RESET_TX_OVFL(); + errorLED(0); + return CR; + } + } + + return BELL; +} + +/** + * Interprets given line and set filter mask + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_setFilterMask(char * line) { + if (state == STATE_CONFIG) + { + unsigned long am0, am1, am2, am3; + if (parseHex(&line[1], 2, &am0) && parseHex(&line[3], 2, &am1) && parseHex(&line[5], 2, &am2) && parseHex(&line[7], 2, &am3)) { + can_set_SJA1000_filter_mask(am0, am1, am2, am3); + return CR; + } + } + + return BELL; +} + +/** + * Interprets given line and set filter code + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_setFilterCode(char * line) { + if (state == STATE_CONFIG) + { + unsigned long ac0, ac1, ac2, ac3; + if (parseHex(&line[1], 2, &ac0) && parseHex(&line[3], 2, &ac1) && parseHex(&line[5], 2, &ac2) && parseHex(&line[7], 2, &ac3)) { + can_set_SJA1000_filter_code(ac0, ac1, ac2, ac3); + return CR; + } + } + return BELL; +} + +/** + * Interprets given line and jump to bootloader + * + * @param line Line string which contains the transmit command + */ +unsigned char parseCmd_bootloaderJump(char * line) { + + unsigned long magic; + if (parseHex(&line[1], 2, &magic)) { + + // check magic code and if bootloader version is new enough to supports bootloader entry via jump + if (magic == 0x10) { + CANCON = 0x80; + INTCON = 0; + #asm + goto BOOTLOADER_ENTRY_ADDRESS + #endasm + } + } + + return BELL; +} + + +/** + * Parse given command line + * + * @param line Line string to parse + */ +void parseLine(char * line) { + + unsigned char result = BELL; + + switch (line[0]) { + case 'S': // Setup with standard CAN bitrates + if (state == STATE_CONFIG) + { + switch (line[1]) { + case '0': can_set_bittiming(CAN_TIMINGS_10K); result = CR; break; + case '1': can_set_bittiming(CAN_TIMINGS_20K); result = CR; break; + case '2': can_set_bittiming(CAN_TIMINGS_50K); result = CR; break; + case '3': can_set_bittiming(CAN_TIMINGS_100K); result = CR; break; + case '4': can_set_bittiming(CAN_TIMINGS_125K); result = CR; break; + case '5': can_set_bittiming(CAN_TIMINGS_250K); result = CR; break; + case '6': can_set_bittiming(CAN_TIMINGS_500K); result = CR; break; + case '7': can_set_bittiming(CAN_TIMINGS_800K); result = CR; break; + case '8': can_set_bittiming(CAN_TIMINGS_1M); result = CR; break; + } + + } + break; + case 's': // Setup with user defined timing settings for CNF1/CNF2/CNF3 + result = parseCmd_setupUserdefined(line); + break; + + case 'G': // Read given register + result = parseCmd_readRegister(line); + break; + + case 'W': // Write given register + result = parseCmd_writeRegister(line); + break; + + case 'V': // Get hardware version + { + + cmdPush('V'); + sendByteHex(VERSION_HARDWARE_MAJOR); + sendByteHex(VERSION_HARDWARE_MINOR); + result = CR; + } + break; + case 'v': // Get firmware version + { + + cmdPush('v'); + sendByteHex(VERSION_FIRMWARE_MAJOR); + sendByteHex(VERSION_FIRMWARE_MINOR); + result = CR; + } + break; + case 'N': // Get serial number + { + cmdPush('N'); + cmdPush('1'); + cmdPush('9'); + cmdPush('7'); + cmdPush('6'); + result = CR; + } + break; + case 'O': // Open CAN channel + if (state == STATE_CONFIG) { + // normal mode + CANCON = 0x00; + while ((CANSTAT & 0xE0) != 0x00) continue; + + clock_reset(); + + state = STATE_OPEN; + result = CR; + } + break; + + case 'l': // Loop-back mode + if (state == STATE_CONFIG) { + CANCON = 0x40; + while ((CANSTAT & 0xE0) != 0x40) continue; + + state = STATE_OPEN; + result = CR; + } + break; + + case 'L': // Open CAN channel in listen-only mode + if (state == STATE_CONFIG) { + CANCON = 0x60; + while ((CANSTAT & 0xE0) != 0x60) continue; + + state = STATE_LISTEN; + result = CR; + } + break; + + case 'C': // Close CAN channel + if (state != STATE_CONFIG) { + CANCON = 0x80; + while ((CANSTAT & 0xE0) != 0x80) continue; + + state = STATE_CONFIG; + result = CR; + } + break; + + case 'r': // Transmit standard RTR (11 bit) frame + case 'R': // Transmit extended RTR (29 bit) frame + case 't': // Transmit standard (11 bit) frame + case 'T': // Transmit extended (29 bit) frame + if (state == STATE_OPEN) { + if (parseCmd_transmit(line)) { + if (line[0] < 'Z') cmdPush('Z'); + else cmdPush('z'); + result = CR; + } + } + break; + + case 'f': // Handle error reporting requests + result = parseCmd_errorReporting(line); + break; + + case 'F': // Handle status flag requests + result = parseCmd_errorFlags(line); + break; + + case 'Z': // Set time stamping + result = parseCmd_setTimestamping(line); + break; + + case 'm': // Set accpetance filter mask + result = parseCmd_setFilterMask(line); + break; + + case 'M': // Set accpetance filter code + result = parseCmd_setFilterCode(line); + break; + + case 'B': // Jump to bootloader + result = parseCmd_bootloaderJump(line); + break; + } + + cmdPush(result); + + cmdSend(); +} + +uint8_t canmsg2ascii(canmsg_t * canmsg, char * s) { + char ch, step = RX_STEP_TYPE; + uint8_t num = 0; + + do { + if (step == RX_STEP_TYPE) { + // type + if (canmsg->flags.extended) { + step = RX_STEP_ID_EXT; + if (canmsg->flags.rtr) *s++ = 'R'; + else *s++ = 'T'; + num++; + } else { + step = RX_STEP_ID_STD; + if (canmsg->flags.rtr) *s++ = 'r'; + else *s++ = 't'; + num++; + } + } else if (step < RX_STEP_DLC) { + // id + unsigned char i = step - 1; + unsigned char * id_bp = (unsigned char*) &canmsg->id; + + ch = id_bp[3 - (i / 2)]; + if ((i % 2) == 0) ch = ch >> 4; + + ch = ch & 0xF; + if (ch > 9) ch = ch - 10 + 'A'; + else ch = ch + '0'; + + *s++ = ch; + num++; + + step++; + } else if (step < RX_STEP_DATA) { + // length + ch = canmsg->dlc; + + ch = ch & 0xF; + if (ch > 9) ch = ch - 10 + 'A'; + else ch = ch + '0'; + + *s++ = ch; + num++; + + if ((canmsg->dlc == 0) || canmsg->flags.rtr) step = RX_STEP_TIMESTAMP; + else step++; + } else if (step < RX_STEP_TIMESTAMP) { + // data + unsigned char i = step - RX_STEP_DATA; + + ch = canmsg->data[i / 2]; + if ((i % 2) == 0) ch = ch >> 4; + + ch = ch & 0xF; + if (ch > 9) ch = ch - 10 + 'A'; + else ch = ch + '0'; + + *s++ = ch; + num++; + + step++; + if (step - RX_STEP_DATA == canmsg->dlc*2) step = RX_STEP_TIMESTAMP; + } else if (timestamping && (step < RX_STEP_CR)) { + // timestamp + unsigned char i = step - RX_STEP_TIMESTAMP; + + if (i < 2) ch = (canmsg->timestamp >> 8) & 0xff; + else ch = canmsg->timestamp & 0xff; + if ((i % 2) == 0) ch = ch >> 4; + + ch = ch & 0xF; + if (ch > 9) ch = ch - 10 + 'A'; + else ch = ch + '0'; + + *s++ = ch; + num++; + + step++; + } else { + // linebreak + *s = CR; + num++; + + step = RX_STEP_FINISHED; + } + } while (step != RX_STEP_FINISHED); + + return num; +} diff --git a/frontend.h b/frontend.h new file mode 100644 index 0000000..6cd49bc --- /dev/null +++ b/frontend.h @@ -0,0 +1,28 @@ +#ifndef _FRONTEND_ +#define _FRONTEND_ + +#define LINE_MAXLEN 100 +#define BELL 7 +#define CR 13 +#define LR 10 + +#define RX_STEP_TYPE 0 +#define RX_STEP_ID_EXT 1 +#define RX_STEP_ID_STD 6 +#define RX_STEP_DLC 9 +#define RX_STEP_DATA 10 +#define RX_STEP_TIMESTAMP 26 +#define RX_STEP_CR 30 +#define RX_STEP_FINISHED 0xff + +extern unsigned char transmitStd(char *); +extern void parseLine(char *); +extern char canmsg2ascii_getNextChar(canmsg_t *, unsigned char *); +extern void sendbuffer_send(void); +extern unsigned char sendbuffer_isEmpty(void); +extern void sendStatusflags(unsigned char); +extern void frontend_sendErrorflags(unsigned char); + +extern unsigned char canmsg2ascii(canmsg_t *, char *); + +#endif diff --git a/images/ELM327SLCAN.png b/images/ELM327SLCAN.png new file mode 100644 index 0000000..597bb0b Binary files /dev/null and b/images/ELM327SLCAN.png differ diff --git a/images/cmd.png b/images/cmd.png new file mode 100644 index 0000000..92e9542 Binary files /dev/null and b/images/cmd.png differ diff --git a/images/filtering.png b/images/filtering.png new file mode 100644 index 0000000..a2b0382 Binary files /dev/null and b/images/filtering.png differ diff --git a/init.c b/init.c new file mode 100644 index 0000000..4a60a17 --- /dev/null +++ b/init.c @@ -0,0 +1,89 @@ +#include +#include +#include "elm327slcan.h" + +void init(void) { + LATA = 0; +#if defined(__DEBUG) + TRISA &= 0b11010000; +#endif + ANCON0 = 0; // Digital port + ANCON1 &= 0xF8; // Digital port + + // EUSART init + UxBAUDCON = 0b01001000; + UxSPBRGH = BRGVALUE >> 8; + UxSPBRG = BRGVALUE & 255; + UxTXSTA = 0b00100110; + UxRCSTA = 0b10010000; + + // ECAN init +#if defined(__DEBUG) + LATC = 0x40; // RC6 as CANTX output + TRISC = 0x80; // RC7 as CANRX input + TRISB6 = 0; +#else + LATB = 0b11000100; // RB2 as CANTX output + TRISB = 0b00111011; // RB3 as CANRX input + TRISC6 = 0; +#endif + CANCON = 0x80; // Configuration mode + while ((CANSTAT & 0xE0) != 0x80) continue; + BSEL0 = 0; // Buffer 5 to 0 are configured in Receive mode +// BRGCON1 = 1; // Tq = 1/4 us, SJW = 1 +// BRGCON2 = 0b10001010; // SEG1PH = 2, PRSEG = 3 +// BRGCON3 = 1; // SEG2PH = 2 + CIOCON = 0b00100000; + ECANCON = 0b10110000; // Mode 2 + + RXM0SIDH = 0xFF; + //RXM0SIDL = 0xEB; + RXM0SIDL = 0xE3; + RXM0EIDH = 0xFF; + RXM0EIDL = 0xFF; + RXM1SIDH = 0xFF; + //RXM1SIDL = 0xEB; + RXM1SIDL = 0xE3; + RXM1EIDH = 0xFF; + RXM1EIDL = 0xFF; + + uint8_t i, *ptr; + + i = 24; + ptr = &RXF0SIDH; + do { + *ptr++ = 0; + } while (--i); + + i = 40; + ptr = &RXF6SIDH; + do { + *ptr++ = 0; + } while (--i); + + IPEN = 1; + + // clock + CCP1IP = 0; + CCPR1H = 0x0F; + CCPR1L = 0xA0; + CCP1CON = 0x0B; + CCP1IE = 1; + + CCP2IP = 0; + CCPR2H = 0xC3; + CCPR2L = 0x50; + CCP2CON = 0x0B; + C2TSEL = 1; + CCP2IE = 1; + + T1CON = 1; + T3CON = 0x31; + + // uart + UxRCIE = 1; + + stateLED(0); + + INTCON = 0xC0; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..eb22cc6 --- /dev/null +++ b/main.c @@ -0,0 +1,73 @@ +#include +#include +#include "clock.h" +#include "can.h" +#include "frontend.h" +#include "elm327slcan.h" +#include "rbuf.h" + +extern void init(void); + +#define RSIZE 128 +#define SSIZE 248 + +char rBuf[RSIZE]; +char sBuf[SSIZE]; + +rbuf_t rRing = { rBuf, 0, 0, RSIZE }; +rbuf_t sRing = { sBuf, 0, 0, SSIZE }; + +uint8_t state = STATE_CONFIG; + +void __interrupt(high_priority) myIsr(void) { + uint8_t data; + + if (UxTXIE && UxTXIF) { + if (rbuf_pop_isr((rbuf_t *)&sRing, &data)) + UxTXREG = data; + else + cmdStop(); + } + + if (UxRCIF && UxRCIE) { + if (!rbuf_push_isr((rbuf_t *)&rRing, UxRCREG)) + SET_TX_OVFL(); + } +} + +int main(void) { + uint8_t ch, num, txtbuf[32]; + char line[LINE_MAXLEN]; + uint8_t linepos = 0; + + canmsg_t canmsg_buffer; + + init(); + + while (1) { + if (state != STATE_CONFIG) { + while (can_receive_message(&canmsg_buffer)) { + num = canmsg2ascii(&canmsg_buffer, &txtbuf); + if (num < rbuf_free_items((rbuf_t *)&sRing)) { + for (ch = 0; ch < num; ch++) { + rbuf_push((rbuf_t *)&sRing, txtbuf[ch]); + } + cmdSend(); + } + else SET_RX_OVFL(); + } + } + + while (rbuf_pop((rbuf_t *)&rRing, &ch)) { + if (ch == CR) { + line[linepos] = 0; + parseLine(line); + linepos = 0; + } else if (ch != LR) { + line[linepos] = ch; + if (linepos < LINE_MAXLEN - 1) linepos++; + } + } + } + return 0; +} diff --git a/out/ELM327SLCAN.hex b/out/ELM327SLCAN.hex new file mode 100644 index 0000000..6393e29 --- /dev/null +++ b/out/ELM327SLCAN.hex @@ -0,0 +1,313 @@ +:040000006FEF00F0AE +:060008004D8260EF08F0DC +:100018004D80D8CF0CF0E8CF0DF0E0CF0EF0A4B2B1 +:10002800A3A20BD0A492484A492A600E481805E1B9 +:10003800EA0E491802E1486A496AA4B4A3A443D065 +:10004800A4944A281F0B4A6E0F6A4A5004E04C5089 +:1000580002E1010E0F6E0FB002D08A9E01D08A8E87 +:10006800ABA202D08A9C2FD071AC0ED0010E106EBC +:100078004AA204D0040E4A6001D0106A10B002D01F +:100088008A9C01D01FD01FD089A40ED0010E116EFA +:100098004AA204D0080E4A6001D0116A11B002D0F9 +:1000A8008A9C01D00FD00FD089A60DD0010E126EF8 +:1000B8004AA204D00C0E4A6001D0126A12B002D0D3 +:1000C8008A9C01D08A8C0EC0E0FF0DC0E8FF0CC0EE +:1000D800D8FF4D901000B8EF08F0070E606F2ED1D2 +:1000E8004C5049E062D19E0E136E030E146E310E11 +:1000F8009DEC09F00D0E20D18C0E136E010E146EBE +:10010800270E9DEC09F0F6D78C0E136E010E146EB7 +:100118000F0E9DEC09F0EED78C0E136E010E146EC7 +:10012800070E9DEC09F0E6D78A0E136E010E146EC9 +:10013800070E9DEC09F0DED78A0E136E010E146EC1 +:10014800030E9DEC09F0D6D78A0E136E010E146EBD +:100158009DEC09F0CFD78C0E136E010E146E000EB5 +:100168009DEC09F0C7D78A0E136E010E146E000EAF +:100178009DEC09F0BFD73A50D96EDA6AD92ADF5018 +:10018800300AB1E0010AB8E0030ABEE0010AC4E09F +:10019800070ACAE0010AD0E0030AD6E0010ADBE058 +:1001A8000F0AE1E002D13AC01AF08DEC08F0C4D091 +:1001B8003AC01BF042EC09F0BFD03AC01AF009EC83 +:1001C80009F0BAD0410E136E560E146E18EC07F0F3 +:1001D800000901E18984010EE1EC08F0000EE1EC70 +:1001E80008F088D7410E136E760E146E18EC07F0DF +:1001F800000901E18984010EE1EC08F0080EE1EC48 +:1002080008F078D7410E136E4E0E146E18EC07F0F6 +:10021800000901E18984410E136E310E146E18EC49 +:1002280007F0000901E18984410E136E390E146E3E +:1002380018EC07F0000901E18984410E136E370EAE +:10024800146E18EC07F0000901E18984410E136E61 +:10025800360E146E18EC07F0000901E04BD78984BC +:1002680049D74C5001E0A1D06F6A6ECF3BF0E00E49 +:100278003B16FBE1B7EC09F0010E4C6E3BD74C5036 +:1002880001E093D0400E6F6E6E50E00B400AFCE127 +:10029800010E0CD0606F89D04C5001E086D0600E02 +:1002A8006F6E6E50E00B600AFCE1020E4C6E0D0E94 +:1002B800F1D74C507AE0800E6F6E6E50E00B800ADA +:1002C800FCE14C6A17D74C0470E13AC020F0C4EC4A +:1002D80004F000096AE03A50D96EDA6A5A0EDF6013 +:1002E80007D0410E136E5A0E146E18EC07F0B4D7EF +:1002F800410E136E7A0E146E18EC07F0ADD73AC0A3 +:100308001AF028EC09F018D03AC01BF082EC09F07A +:1003180013D03AC01AF06EEC09F00ED03AC01DF0B6 +:100328008AEC07F009D03AC01AF0C2EC07F004D002 +:100338003AC01AF058EC09F00001ACD73A50D96E1F +:10034800DA6ADF50420AF4E0010AB3E0050ADCE0A9 +:10035800010A01E12DD70B0A9FE0010AE4E0030A34 +:1003680001E150D7010A01E17CD71D0AACE0010A7E +:1003780001E1B6D6070AA7E0020A01E123D7010A7C +:1003880001E11BD70D0AC5E03C0AB9E00A0A01E100 +:1003980076D7010AC3E01F0A96E0010A01E103D7F4 +:1003A800070A91E0020A01E11DD7410E136E60C0F1 +:1003B80014F018EC07F0000901E189849D88120007 +:1003C80071AE000C92EC09F01550D96EDA6A0E0E77 +:1003D800D92613C0DEFF14C0DDFFE00E72166F5081 +:1003E800070B1009721260AE000C61CF1DF062CFCE +:1003F80021F021A692D01550D96EDA6A040ED926BA +:10040800DF821DC016F0176A186A196A150ED8908F +:100418001636173618361936E82EF9D71550D96E0C +:10042800DA6A16C0DEFF17C0DEFF18C0DEFF19C08B +:10043800DEFF21C016F0E00E161616C017F0186A77 +:10044800196A1A6A0D0ED8901736183619361A36E0 +:10045800E82EF9D71550D96EDA6A1750DE121850FF +:10046800DE121950DE121A50DE1221C016F0030EE9 +:10047800161616C017F0186A196A1A6A18C01AF000 +:1004880017C019F0186A176A1550D96EDA6A17502A +:10049800DE121850DE121950DE121A50DE1263CF27 +:1004A80016F0176A186A196A18C019F017C018F0F8 +:1004B80016C017F0166A1550D96EDA6A1650DE1291 +:1004C8001750DE121850DE121950DE121550D96E70 +:1004D800DA6A6450DE12000EDE12DE12DE1265CF1A +:1004E8001EF01550D96EDA6A050ED9261E500F0B6C +:1004F800DF6E1EC016F0060E176ED8901632172E35 +:10050800FCD7010E16161550D96EDA6A040ED926D4 +:10051800DF501618FE0B161841D01550D96EDA6A3E +:10052800040ED926DF9221C016F0163A0F0E1616C1 +:10053800010E16161550D96EDA6A040ED926DF5048 +:100548001618FE0B1618DF6E1DC016F0176A186A0B +:10055800196A030ED8901636173618361936E82E4B +:10056800F9D71550D96EDA6A16C0DEFF17C0DEFF5C +:1005780018C0DEFF19C0DEFF1550D96EDA6A2138BF +:10058800E842070BDE12000EDE12DE12DE121550F4 +:10059800D96EDA6A050ED92665500F0BDF6E155035 +:1005A800D96EDA6A040ED926DFB023D01550D96E79 +:1005B800DA6A050ED926DF50206E080E206401D0B5 +:1005C800206E660E1B6E0F0E1C6E1F6A0FD01BC0AE +:1005D800D9FF1CC0DAFF15501F24E16EE26A060E2F +:1005E800E126DFCFE7FF1B4A1C2A1F2A20501F5C89 +:1005F800EEE3609E00F07792010C1E6A1D6A1E50A1 +:1006080031E11350D96EDA6A040ED926DFA217D069 +:10061800010E1E6E1350D96EDA6A040ED926DFA0B9 +:1006280007D01450D96EDA6A520EDF6E142A05D03C +:100638001450D96EDA6A540EF8D71D2AD3D0060E94 +:100648001E6E1350D96EDA6A040ED926DFA005D0C3 +:100658001450D96EDA6A720EE8D71450D96EDA6A75 +:10066800740EE3D7090E1E6023D01E041A6E13C041 +:1006780019F01AC015F0D89015321550195CD96EBA +:10068800DA6A030ED926DF501F6E1AB09ED01F3AC1 +:100698000F0E1F169AD0370E01D0300E1F26145099 +:1006A800D96EDA6A1FC0DFFF142A1D2A1E2A9AD0C3 +:1006B8000A0E1E6028D01350D96EDA6A050ED926A4 +:1006C800DF501F6E0F0E1F16090E1F6402D0370E63 +:1006D80001D0300E1F261450D96EDA6A1FC0DFFF12 +:1006E800142A1D2A1350D96EDA6A050ED926DF504E +:1006F80007E01350D96EDA6A040ED926DFA0D6D7E0 +:100708001A0E1E6E6FD01A0E1E603DD01E50F60FC8 +:100718001B6E1BC015F0D890153213501524D96ED6 +:10072800DA6A060ED926DF501F6E1BB003D01F3AB7 +:100738000F0E1F160F0E1F16090E1F6402D0370E5C +:1007480001D0300E1F261450D96EDA6A1FC0DFFFA1 +:10075800142A1D2A1E2A1350D96EDA6A050ED926C4 +:10076800DF50020DF60E156E16681E50176E186AC9 +:100778001550172616501822F350171833E1F45065 +:100788001818BEE02FD04B5026E01E0E1E6023D056 +:100798001E50E60F1C6E020E1C600ED01350D96E50 +:1007A800DA6A0E0ED926DECF15F0DDCF16F016C0A8 +:1007B80015F0166A155006D01350D96EDA6A0E0E67 +:1007C800D926DF501F6E1CA062D70F0E1F16090E08 +:1007D8001F6463D760D71450D96EDA6A0D0EDF6EC6 +:1007E8001D2A1E681E2801E00AD71D501200E00EBF +:1007F8007216050E721260A606D0720660A603D0A5 +:10080800720660B6000C1350D96EDA6A050ED92646 +:10081800DF501F6E080E1F6401D01F6E1350D96E73 +:10082800DA6A040ED926DFA25FD01350D96EDA6ACD +:10083800DECF14F0DECF15F0DECF16F0DECF17F0E6 +:10084800160E05D0D8901732163215321432E82E0B +:10085800F9D71450616E1350D96EDA6ADECF14F0EE +:10086800DECF15F0DECF16F0DECF17F0110E05D073 +:10087800D8901732163215321432E82EF9D7030EF3 +:1008880014161350D96EDA6ADECF18F0DECF19F0DD +:10089800DECF1AF0DECF1BF00E0E05D0D8901B323B +:1008A8001A3219321832E82EF9D71850E00B141002 +:1008B8000809626E1350D96EDA6ADECF14F0DECF03 +:1008C80015F0DECF16F0DECF17F015C014F016C005 +:1008D80015F017C016F0176A1450636E1350D96ECE +:1008E800DA6ADF50646E23D01350D96EDA6ADECF2D +:1008F80014F0DECF15F0DECF16F0DECF17F0040EC1 +:1009080005D0D8901732163215321432E82EF9D79E +:100918001450616E1350D96EDA6ADF50146E050EEA +:10092800156ED8901436152EFCD71450626E1FC061 +:1009380065FF1350D96EDA6A040ED926DFA004D0F9 +:100948001F504009656E1AD01F5018E0660E1C6EC5 +:100958000F0E1D6E1E6A0FD013501E24D96EDA6A50 +:10096800060ED9261CC0E1FF1DC0E2FFDFCFE7FF5E +:100978001C4A1D2A1E2A1F501E5CEEE36086010CCD +:10098800010E226E2050D96EDA6A720EDE1807E068 +:100998002050D96EDA6A520EDE1801E0226A22B0BF +:1009A80002D02E9001D02E802050D96EDA6A5A0ECD +:1009B800DF6003D02E82080E02D02E92030E246E22 +:1009C8002028136E24C014F0260E156E96EC05F040 +:1009D800000901E1000C26C02AF027C02BF028C02E +:1009E8002CF029C02DF020502424216E2128136ECC +:1009F800010E146E260E156E96EC05F0000901E145 +:100A0800000C26C02FF02EB026D02FC023F0080EE1 +:100A1800236401D0236E256A1BD02550020D205077 +:100A2800F3242424020F136E020E146E260E156E84 +:100A380096EC05F0000901E1000C25C021F0060E36 +:100A4800212621502A0FD96EDA6A26C0DFFF252A0F +:100A58002350255CE2E32A0E136EFBEF03F0896A4C +:100A68000F015D6BF80E5C17480EA76E7D6A670E66 +:100A7800AF6E260EAC6E900EAB6EC40E8A6E3B0E39 +:100A8800936E949C800E6F6E6E50E00B800AFCE1B2 +:100A98000E017D6B200E706EB00E726EF869E30E5B +:100AA800F96FFA69FB69FC69E30EFD6FFE69FF697E +:100AB800180E136EE00E146E0E0E156E14C0D9FFCC +:100AC80015C0DAFFDF6A144A152A132EF7D7280E45 +:100AD800136E480E146E0E0E156E14C0D9FF15C095 +:100AE800DAFFDF6A144A152A132EF7D7D08EA5929B +:100AF8000F0EBD6EA00EBC6E0B0EBB6EA382A5942E +:100B0800C30E0F01526F500E516F0B0E506F99822A +:100B1800A384010ECD6E310EB16E9D8A8A8EC00EF1 +:100B2800F26EC00C1550D96EDA6ADE6ADE6ADE6AC9 +:100B3800DD6A53D01350D96EDA6ADF5001E1000C38 +:100B48001550D96EDA6A040ED890DE36DE36DE36F7 +:100B5800DD36D906D906E82EF7D71350D96EDA6AEA +:100B68002F0EDF6408D01350D96EDA6A3A0EDF60B0 +:100B780002D0D00E1BD01350D96EDA6A400EDF6453 +:100B880008D01350D96EDA6A470EDF6002D0C90E5A +:100B98000DD01350D96EDA6A600EDF64000C135062 +:100BA800D96EDA6A670EDF60000CA90E166E176838 +:100BB8001350D96EDA6ADF50186E196A1650182663 +:100BC800175019221550D96EDA6A1850DE261950B6 +:100BD800DE22000EDE22DE22132A14061428AAE1E1 +:100BE800010C0001E66B33EC05F04C5023E146D0D4 +:100BF800E80E136EC50E146E01EC03F00001E56FEC +:100C0800410E136EFAEC07F0E56113D0E76B0CD0D8 +:100C1800410E136EE751C50FD96EDA6ADF50146EB4 +:100C280018EC07F00001E72BE551E75DF1E39D883B +:100C380001D08984E80E156EE4EC01F000091EE08D +:100C4800D7D70D0E0001E7190CE1E651610FD96EF7 +:100C5800DA6ADF6A610E3A6E71EC00F00001E66B49 +:100C68000DD00A0EE7190AE0E651610FD96EDA6A6B +:100C7800E7C0DFFF630EE66101D0E62B3C0E136E82 +:100C8800E70E146E96EC06F00009AFE0DAD71C6E9A +:100C98001C1C0E01F86F131C166ED8901632D890D3 +:100CA8001632D8901632030E1616131CE00B1610C7 +:100CB800F96F15B0F987131C166E050E176ED890CC +:100CC8001636172EFCD71450186E196A181C1A6E8F +:100CD800191C1B6E1B341B321A321B341B321A327E +:100CE8001B341B321A321A501610FA6F141C166E67 +:100CF800050E176ED8901636172EFCD71550186E9D +:100D0800196A181C1A6E191C1B6E1B341B321A32F6 +:100D18001B341B321A321B341B321A321A5016106B +:100D2800FB6F12001350D96EDA6A030ED9261350DE +:100D3800E16EE26A020EE126DE50E61801E1000CDF +:100D48001350D96EDA6A030ED926DF50156E1528AE +:100D5800186E1350D96EDA6A040ED926DE50185C64 +:100D680001E3186A1350D96EDA6A030ED926DF50E8 +:100D7800156E1350D96EDA6ADECF16F0DDCF17F094 +:100D880015501624D96E000E1720DA6E1450E16E35 +:100D9800E26ADFCFE7FF1350D96EDA6A030ED9266D +:100DA80018C0DFFF010C0150D96EDA6A030ED9268C +:100DB8000150E16EE26A020EE126DE50E61801E11A +:100DC800000C0150D96EDA6A030ED926DF50036E83 +:100DD8000328066E0150D96EDA6A040ED926DE5051 +:100DE800065C01E3066A0150D96EDA6A030ED92659 +:100DF800DF50036E0150D96EDA6ADECF04F0DDCF22 +:100E080005F003500424D96E000E0520DA6E025056 +:100E1800E16EE26ADFCFE7FF0150D96EDA6A030EAE +:100E2800D92606C0DFFF010C1350D96EDA6A020E0C +:100E3800D926DF50156E1528186E1350D96EDA6A48 +:100E4800040ED926DE50185C01E3186A1350D96ED7 +:100E5800DA6A030ED9261850DE1801E1000C135087 +:100E6800D96EDA6A020ED926DF50156E1350D96E84 +:100E7800DA6ADECF16F0DDCF17F015501624D96EDA +:100E8800000E1720DA6E14C0DFFF1350D96EDA6A2D +:100E9800020ED92618C0DFFF010C0150D96EDA6A9C +:100EA800020ED926DF50036E0328066E0150D96E54 +:100EB800DA6A040ED926DE50065C01E3066A0150A0 +:100EC800D96EDA6A030ED9260650DE1801E1000C45 +:100ED8000150D96EDA6A020ED926DF50036E01502E +:100EE800D96EDA6ADECF04F0DDCF05F003500424B2 +:100EF800D96E000E0520DA6E02C0DFFF0150D96EF0 +:100F0800DA6A020ED92606C0DFFF010C4C5035E123 +:100F18001D28136E020E146E1E0E156E96EC05F04B +:100F280000092BE01D50030F136E020E146E220EE3 +:100F3800156E96EC05F0000920E01D50050F136EA4 +:100F4800020E146E260E156E96EC05F0000915E0DB +:100F58001D50070F136E020E146E2A0E156E96ECB6 +:100F680005F000090AE022C013F026C014F02AC0D8 +:100F780015F01E504BEC06F00D0C070C4C5035E1EB +:100F88001A28136E020E146E1B0E156E96EC05F0E1 +:100F980000092BE01A50030F136E020E146E1F0E79 +:100FA800156E96EC05F0000920E01A50050F136E37 +:100FB800020E146E230E156E96EC05F0000915E06E +:100FC8001A50070F136E020E146E270E156E96EC4C +:100FD80005F000090AE01FC013F023C014F027C071 +:100FE80015F01B5031EC08F00D0C070C1350D96E9E +:100FF800DA6A030ED9261350E16EE26A020EE12680 +:10100800DE50E65C19E31350D96EDA6A020ED9266F +:10101800DF50146E1350D96EDA6A040ED926DF50E9 +:10102800156E1450155C166E1350D96EDA6A030EDD +:10103800D926DF50162412001350D96EDA6A020E30 +:10104800D926DF50146E1350D96EDA6A030ED926EA +:10105800DF50156E1450155C1200186E18C0E0FEB3 +:1010680013C016F0D8901632D8901632D89016328F +:10107800030E16161350E00B16100E01E16F15B093 +:10108800E18713C016F0050E176ED8901636172E86 +:10109800FCD71438E8461F0B1610E26F14C016F080 +:1010A800050E176ED8901636172EFCD71538E84659 +:1010B8001F0B1610E36F1200E1CF07F0E2CF08F024 +:1010C800D9CF09F0DACF0AF09DB89EA80CD0410E0E +:1010D800016E0B0E026ED7EC06F0000903E00BC0A0 +:1010E800ADFF01D09D989EBA9DAA09D03C0E016E15 +:1010F800AECF02F051EC07F0000901E189860AC081 +:10110800DAFF09C0D9FF08C0E2FF07C0E1FF4D922E +:1011180011004C5028E11A28136E020E146E1B0E93 +:10112800156E96EC05F000091EE01A50030F136EB9 +:10113800020E146E1F0E156E96EC05F0000913E0F2 +:101148001A50050F136E020E146E230E156E96ECD0 +:1011580005F0000908E01FC013F023C014F01B506D +:101168009DEC09F00D0C070C640EF66E130EF76E6D +:10117800000EF86E00EE3CF010EE0AF00900F5CF14 +:10118800EEFFE550E150FAE102EE00F0F80EEE6AEB +:10119800E806FDE101EE00F0800EEE6AE806FDE1EA +:1011A80000EE46F0070EEE6AE806FDE14D904D921E +:1011B800000EF86E0001F5EF05F0196E19380F0BE7 +:1011C8001A6E090E1A6402D0370E01D0300E1A2694 +:1011D800410E136E1AC014F018EC07F0000901E173 +:1011E800898419500F0B1A6E090E1A6402D0370E33 +:1011F80001D0300E1A26410E136E1AC014F018ECE6 +:1012080007F00009D8B4898412001A28136E030E57 +:10121800146E1B0E156E96EC05F0000914E01A50BA +:10122800040F136E020E146E1F0E156E96EC05F069 +:10123800000909E01BC013F01CC014F01FC015F012 +:10124800A5EC09F00D0C070C1B6A1C6A1D6A1E6AC6 +:101258001A28136E010E146E1B0E156E96EC05F00F +:1012680000090BE0020E1B181C101D101E1005E1D2 +:10127800719C899489968A8C0D0C070C1B28136E17 +:10128800030E146E1D0E156E96EC05F000090BE0AA +:101298001DC013F01EC014F0ACEC09F01C6E1C50FD +:1012A800E1EC08F00D0C070C1A28136E020E146EF0 +:1012B8001B0E156E96EC05F000090BE0100E1B18BE +:1012C8001C101D101E1005E1800E6F6EF26A80EF73 +:1012D8003EF0070C1A28136E010E146E1B0E156EC5 +:1012E80096EC05F0000909E01B501C101D101E109B +:1012F800010ED8B4000E4B6E0D0C070C71507F0B0D +:101308001C6E410E136E460E146E18EC07F00009A1 +:1013180001E189841C50E1EC08F00D0CA39248C04F +:1013280046F049C047F0A38246C013F047C014F006 +:101338001200156E15C043FE13C044FE14C045FECE +:10134800120013C0E9FF14C0EAFF15C0EFFF120036 +:1013580013C0E9FF14C0EAFFEF50120000010000BB +:101368008000020000F8A392486A496AA38212002A +:00000001FF diff --git a/rbuf.c b/rbuf.c new file mode 100644 index 0000000..1a2403b --- /dev/null +++ b/rbuf.c @@ -0,0 +1,70 @@ +#include "rbuf.h" + +uint8_t rbuf_push(rbuf_t *c, uint8_t data) { + uint8_t next; + + next = c->head + 1; // next is where head will point to after this write. + if (next >= c->maxlen) + next = 0; + + if (next == c->tail) // if the head + 1 == tail, circular buffer is full + return 0; + + c->buffer[c->head] = data; // Load data and then move + c->head = next; // head to next data offset. + return 1; // return success to indicate successful push. +} + +uint8_t rbuf_push_isr(rbuf_t *c, uint8_t data) { + uint8_t next; + + next = c->head + 1; // next is where head will point to after this write. + if (next >= c->maxlen) + next = 0; + + if (next == c->tail) // if the head + 1 == tail, circular buffer is full + return 0; + + c->buffer[c->head] = data; // Load data and then move + c->head = next; // head to next data offset. + return 1; // return success to indicate successful push. +} + +uint8_t rbuf_pop(rbuf_t *c, uint8_t *data) { + uint8_t next; + + if (c->head == c->tail) // if the head == tail, we don't have any data + return 0; + + next = c->tail + 1; // next is where tail will point to after this read. + if(next >= c->maxlen) + next = 0; + + *data = c->buffer[c->tail]; // Read data and then move + c->tail = next; // tail to next offset. + return 1; // return success to indicate successful push. +} + +uint8_t rbuf_pop_isr(rbuf_t *c, uint8_t *data) { + uint8_t next; + + if (c->head == c->tail) // if the head == tail, we don't have any data + return 0; + + next = c->tail + 1; // next is where tail will point to after this read. + if(next >= c->maxlen) + next = 0; + + *data = c->buffer[c->tail]; // Read data and then move + c->tail = next; // tail to next offset. + return 1; // return success to indicate successful push. +} + +uint8_t rbuf_free_items(rbuf_t *c) { + if (c->head >= c->tail) { + uint8_t tmp = c->maxlen - c->head; + return (tmp + c->tail); + } + + return (c->tail - c->head); +} diff --git a/rbuf.h b/rbuf.h new file mode 100644 index 0000000..baed947 --- /dev/null +++ b/rbuf.h @@ -0,0 +1,33 @@ +#ifndef __RBUF_H_ +#define __RBUF_H_ + +#include + +typedef struct { + char *buffer; + uint8_t head; + uint8_t tail; + uint8_t maxlen; +} rbuf_t; + +extern uint8_t rbuf_free_items(rbuf_t *); + +/* + * Method: rbuf_pop + * Returns: + * 1 - Success + * 0 - Empty + */ +extern uint8_t rbuf_pop (rbuf_t *, uint8_t *); +extern uint8_t rbuf_pop_isr (rbuf_t *, uint8_t *); + +/* + * Method: rbuf_push + * Returns: + * 1 - Success + * 0 - Out of space + */ +extern uint8_t rbuf_push (rbuf_t *, uint8_t); +extern uint8_t rbuf_push_isr(rbuf_t *, uint8_t); + +#endif /* __RBUF_H_ */