From c1e41270e0f8d7dc1238b4460fedfa9aa2535d3d Mon Sep 17 00:00:00 2001 From: J62 Date: Tue, 12 Dec 2023 02:16:20 -0800 Subject: [PATCH] initial commit --- ChangeLog | 46 ++++++ Makefile | 53 +++++++ src/comm.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++ src/comm.h | 155 ++++++++++++++++++++ src/config.h | 119 +++++++++++++++ src/global.h | 72 +++++++++ src/io.c | 73 ++++++++++ src/io.h | 48 ++++++ src/main.c | 134 +++++++++++++++++ src/main.h | 54 +++++++ src/memory.c | 295 +++++++++++++++++++++++++++++++++++++ src/memory.h | 109 ++++++++++++++ src/sync_timer.c | 125 ++++++++++++++++ src/sync_timer.h | 55 +++++++ src/usart.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++ src/usart.h | 269 ++++++++++++++++++++++++++++++++++ tools/avr-gcc | 0 tools/colorgcc | 322 ++++++++++++++++++++++++++++++++++++++++ 18 files changed, 2666 insertions(+) create mode 100644 ChangeLog create mode 100644 Makefile create mode 100644 src/comm.c create mode 100644 src/comm.h create mode 100644 src/config.h create mode 100644 src/global.h create mode 100644 src/io.c create mode 100644 src/io.h create mode 100644 src/main.c create mode 100644 src/main.h create mode 100644 src/memory.c create mode 100644 src/memory.h create mode 100644 src/sync_timer.c create mode 100644 src/sync_timer.h create mode 100644 src/usart.c create mode 100644 src/usart.h create mode 100644 tools/avr-gcc create mode 100644 tools/colorgcc diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..7d699bd --- /dev/null +++ b/ChangeLog @@ -0,0 +1,46 @@ +* Version 1.2.0 (20100306) + - adopted to new build system (taken from flyer) + +* Version 1.1.5 (20100306) + - updated usart lib and sync timer + +* Version 1.1.4 (20091212) + - secured firmware command + - rewritten modfify command handling to support new option + - due to flaw in TurboEDIT's byte count in write W command + added flag that decides how to treat bytecount in W command + b_bcnt_mode = false => 0x00 means 256 bytes, 0xFF means 255 bytes + b_bcnt_mode = true => 0x00 means 1 byte, 0xFF means 255 bytes + NOTE: use b_cnt_mode = true only for TurboEDIT! + - set default transmission parameters to: + baudrate 920000, 2 stop bits, even parity + (defaults for CROME 1.5.3 and above) - most frequent usage + NOTE: due to flaw in CROME 1.5.3 and 1.5.5beta (older versions + are not affected) serial port opening methods which discards + any user settings in window and uses 9600 or 920000 baudrate + +* Version 1.1.3 (20091207) + - fixed usart_const computation issue in initUsart + +* Version 1.1.2 (20091206) + - fixed error flags handling in usart module + +* Version 1.1.1 (20091206) + - added response when parameters modification failed + +* Version 1.1.0 (20091124) + - due to compatibility issues default USART frame again is: + baudrate 115200, 2 stop bits, no parity + - general code cleanup + - reduced clock cycles in memory handling + - added new command: M - modify usart settings (using this + allows to change usart parameters on the fly, what is + needed for proper work with software tools from different + suppliers + - fixed acknowledge in for write block X (bytes order) + +* Version 1.0.1 (20091118) + - changed baudrate to 920000, because of bug in CROME newer than 1.5 + +* Version 1.0.0 (20091116) + - initial release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5b0ab4c --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +SRCS=$(wildcard src/*.c) +OBJS=$(subst .c,.o,$(subst src,obj,$(SRCS))) + +CC=tools/avr-gcc +OBJCOPY=avr-objcopy +SIZE=avr-size +MCU=atmega32 + +TARGET=$(shell basename `pwd`) +VERSION=$(shell echo "$(TARGET)" | cut -d "-" -f 2-) build:$(shell date +%Y%m%d) + +CFLAGS_DEF=-DFVERSION="\"$(VERSION)\"" +CFLAGS_MCU=-mmcu=$(MCU) -fomit-frame-pointer +CFLAGS_OPT=-O2 -g0 -pipe -fshort-enums -fpack-struct -s +CFLAGS_STD=-std=c99 -funsigned-char +CFLAGS_WARN=-Wall -Wextra \ + -Wbad-function-cast -Wunused-parameter -Wshadow -Wpointer-arith -Wmissing-declarations \ + -Wmissing-noreturn -Wunreachable-code -Wdeclaration-after-statement -Wundef -Wmissing-braces + +CFLAGS=$(CFLAGS_MCU) $(CFLAGS_OPT) $(CFLAGS_STD) $(CFLAGS_DEF) +LDFLAGS=-Wl,-O1 -lm + +all : $(TARGET).hex info + +clean : + rm -fr obj/*.o $(TARGET) $(TARGET).hex *.tar.bz2 + +dist : clean + @echo -e "\nCREATED: $(TARGET).tar.bz2" + $(shell cd .. ; tar --exclude=$(TARGET).tar.bz2 -cjf $(TARGET)/$(TARGET).tar.bz2 $(TARGET)) + +info : $(TARGET).hex + @echo "built binary sections size:" + @$(SIZE) --target=ihex $(TARGET).hex + +program : $(TARGET).hex + avrdude -p $(MCU) + +obj/%.o : src/%.c + @echo -e "\033[1;34m[CC]: \033[0m$(CC) $< -o $@ -c $(CFLAGS)" + @$(CC) $(CFLAGS) $(CFLAGS_WARN) -c $< -o $@ + +$(TARGET) : $(OBJS) + @echo -e "\033[1;32m[LD]: \033[0m$(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS)" + @$(CC) $(CFLAGS) $(CFLAGS_WARN) $(LDFLAGS) $^ -o $@ + +$(TARGET).hex : $(TARGET) + @echo -n -e "\033[1;33m[HEX]: \033[0m" + $(OBJCOPY) -O ihex -R .eeprom -R .fuse -R .lock $(TARGET) $@ + + @echo -e "\nBUILD COMPLETE!\n" + +.PHONY : all clean dist info program diff --git a/src/comm.c b/src/comm.c new file mode 100644 index 0000000..79af486 --- /dev/null +++ b/src/comm.c @@ -0,0 +1,371 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include + +#include "config.h" +#include "comm.h" +#include "memory.h" +#include "usart.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* GLOBAL: tells how to treat byte_count in write W command */ +BOOL b_bcnt_mode = false; + +/* GLOBAL: indicates device status */ +BOOL b_status_ready = false; + +/* indicates whether last read byte from usart is ok */ +static BOOL b_read_ok = false; + +/* holds latest read byte */ +static BYTE read_byte; + +/* contains information about bytes to read/write in frame */ +static UINT16 byte_count; + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* ----------------------------------------------------------------------- */ +BYTE checksum(BYTE* buffer_ptr, const UINT16 length) +{ + static UINT8 chksum; + static UINT16 buffer_idx; + + chksum = 0; + + /* sum all bytes with no carry */ + for (buffer_idx = 0; buffer_idx < length; ++buffer_idx) + chksum += buffer_ptr[buffer_idx]; + + return (chksum); +} + +/* ----------------------------------------------------------------------- */ +void pcCommandBytecountMode() +{ + /* 1: mode, 2: checksum */ + data_buffer[0] = read_byte; + if (!usartRead(data_buffer + 1, 2, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + /* if checksum is ok, set byte_count computation method flag */ + if (checksum(data_buffer, 2) == data_buffer[2]) + { + b_bcnt_mode = (data_buffer[1])?(true):(false); + /* acknowledge action */ + usartSendByte('O'); + } + /* otherwise inform that parameter modification failed */ + else + usartSendByte('F'); +} + +/* ----------------------------------------------------------------------- */ +void pcCommandFirmware() +{ + if (!usartRead(data_buffer, 1, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + if (read_byte == data_buffer[0]) + { + /* respond with firmware version number ... */ + usartSendString_P((BYTE*)PSTR("honda-rtp, ver. ")); + usartSendString_P((BYTE*)PSTR(FIRMWARE_VERSION)); + + /* ... with compiler and library version number */ + usartSendString_P((BYTE*)PSTR(", avr-libc: ")); + usartSendString_P((BYTE*)PSTR(__AVR_LIBC_VERSION_STRING__)); + usartSendString_P((BYTE*)PSTR(", avr-gcc: ")); + usartSendString_P((BYTE*)PSTR(__VERSION__)); + } +} + +/* ----------------------------------------------------------------------- */ +void pcCommandModify() +{ + UINT16 baudrate; + UINT16 delay; + + /* 1: baudrate hi byte, 2: baudrate lo byte, 3: double speed, 4: two stop bits, 5: parity, 6: checksum */ + data_buffer[0] = read_byte; + if (!usartRead(data_buffer + 1, 6, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + /* if checksum is ok, reinitialize usart */ + if (checksum(data_buffer, 6) == data_buffer[6]) + { + /* acknowledge action and wait for transmission completion */ + usartSendByte('O'); + for (delay = 0; delay < 0xFFFF; ++delay) + NOP_DELAY; + + baudrate = (UINT16)(data_buffer[1] << 8) + (UINT16)(data_buffer[2]); + initUsart(baudrate, data_buffer[3], data_buffer[4], data_buffer[5]); + } + /* otherwise inform that parameter modification failed */ + else + usartSendByte('F'); +} + +/* ----------------------------------------------------------------------- */ +void pcCommandReadBlock() +{ + /* indicate whether device is busy */ + b_status_ready = false; + + /* 1: bytes to read, 2: addr hi byte, 3: addr lo byte, 4: checksum */ + data_buffer[0] = read_byte; + if (!usartRead(data_buffer + 1, 4, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + /* if checksum is ok, read data from memory */ + if (checksum(data_buffer, 4) == data_buffer[4]) + { + /* 0 for 256 bytes of data */ + byte_count = (data_buffer[1])?(data_buffer[1]):(256); + + memoryReadBlock((UINT16)(data_buffer[2] << 8) + (UINT16)(data_buffer[3]), byte_count, data_buffer); + data_buffer[byte_count] = checksum(data_buffer, byte_count); + + /* send data read from memory with checksum */ + usartSend(data_buffer, byte_count + 1); + } + + /* after all enable ECU to read from device */ + setMemoryAccessEcuRead(); + + /* indicate whether device is ready */ + b_status_ready = true; +} + +/* ----------------------------------------------------------------------- */ +void pcCommandReadByte() +{ + /* indicate whether device is busy */ + b_status_ready = false; + + if (!usartRead(data_buffer, 2, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + /* 1: addr hi byte, 2: addr lo byte */ + data_buffer[2] = *(memoryRead((UINT16)(data_buffer[0] << 8) + (UINT16)(data_buffer[1]))); + + usartSendByte(data_buffer[2]); + + /* after all enable ECU to read from device */ + setMemoryAccessEcuRead(); + + /* indicate whether device is ready */ + b_status_ready = true; +} + +/* ----------------------------------------------------------------------- */ +void pcCommandStatus() +{ + /* respond with 'O' when device is ready or with 'B' when busy */ + data_buffer[0] = (b_status_ready)?('O'):('B'); + data_buffer[1] = checksum(data_buffer, 1); + usartSend(data_buffer, 2); +} + +/* ----------------------------------------------------------------------- */ +void pcCommandUnknown() +{ + data_buffer[0] = UNKNOWN; + data_buffer[1] = checksum(data_buffer, 1); + usartSend(data_buffer, 2); +} + +/* ----------------------------------------------------------------------- */ +void pcCommandVersion() +{ + if (!usartRead(data_buffer, 1, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + if (read_byte == data_buffer[0]) + { + data_buffer[1] = ROMULATOR_PROTOCOL_MAJOR; + data_buffer[2] = ROMULATOR_PROTOCOL_MINOR; + data_buffer[3] = ROMULATOR_PROTOCOL_ID; + + usartSend(data_buffer + 1, 3); + } +} + +/* ----------------------------------------------------------------------- */ +void pcCommandVersionResp() +{ + /* WORKAROUND: discard data received as response to version command */ + usartRead(data_buffer, 4, 1); +} + +/* ----------------------------------------------------------------------- */ +void pcCommandWriteW() +{ + /* indicate whether device is busy */ + b_status_ready = false; + + /* 0: command, 1: number of bytes to write, 2: addr hi byte, 3: addr lo byte */ + data_buffer[0] = read_byte; + if (!usartRead(data_buffer + 1, 3, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + /* WORKAROUND: due to flaw in TurboEDIT's byte count computatnion method */ + if (!b_bcnt_mode) + byte_count = (data_buffer[1])?(data_buffer[1]):(256); + else + byte_count = data_buffer[1] + 1; + + /* timeout waiting for byte after 50 ms */ + if (!usartRead((data_buffer + 4), byte_count + 1, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + if (checksum(data_buffer, byte_count + 4) != data_buffer[byte_count + 4]) + return; + + /* try to write block of bytes few times */ + for (UINT8 i = 0; i < ROMULATOR_PROTOCOL_WRITE_RETRIES; ++i) + { + if (memoryWriteBlock((UINT16)(data_buffer[2] << 8) + (UINT16)(data_buffer[3]), byte_count, data_buffer + 4)) + { + /* acknowledge that block was written properly */ + usartSendByte('O'); + break; + } + } + + /* after all enable ECU to read from device */ + setMemoryAccessEcuRead(); + + /* indicate whether device is ready */ + b_status_ready = true; +} + +/* ----------------------------------------------------------------------- */ +void pcCommandWriteX() +{ + static UINT16 wr_checksum; + + /* indicate whether device is busy */ + b_status_ready = false; + + /* 0: command, 1: number of bytes to write, 2: addr hi byte, 3: addr lo byte */ + data_buffer[0] = read_byte; + if (!usartRead(data_buffer + 1, 3, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + byte_count = (data_buffer[1])?(data_buffer[1]):(256); + + /* timeout waiting for byte after 50 ms */ + if (!usartRead((data_buffer + 4), byte_count + 1, ROMULATOR_PROTOCOL_READ_TIMEOUT)) + return; + + if (checksum(data_buffer, byte_count + 4) != data_buffer[byte_count + 4]) + return; + + /* try to write block of bytes few times */ + for (UINT8 i = 0; i < ROMULATOR_PROTOCOL_WRITE_RETRIES; ++i) + { + if (memoryWriteBlock((UINT16)(data_buffer[2] << 8) + (UINT16)(data_buffer[3]), byte_count, data_buffer + 4)) + { + /* acknowledge that block was written properly - two byte checksum */ + wr_checksum = 0; + + for (UINT16 j = 0; j < byte_count; ++j) + wr_checksum += data_buffer[4 + j]; + + data_buffer[0] = wr_checksum; + data_buffer[1] = (wr_checksum >> 8); + + usartSend(data_buffer, 2); + break; + } + } + + /* after all enable ECU to read from device */ + setMemoryAccessEcuRead(); + + /* indicate whether device is ready */ + b_status_ready = true; +} + +/* ----------------------------------------------------------------------- */ +void pcCommand() +{ + read_byte = usartReadByte(&b_read_ok); + + if (b_read_ok) + { + /* treat each first incoming byte as command */ + switch (read_byte) + { + case BYTECOUNT_MODE: + pcCommandBytecountMode(); + + break; + case FIRMWARE: + pcCommandFirmware(); + break; + + case MODIFY: + pcCommandModify(); + break; + + case READ_BLOCK: + pcCommandReadBlock(); + break; + + case READ_BYTE: + pcCommandReadByte(); + break; + + case STATUS: + pcCommandStatus(); + break; + + case VERSION: + pcCommandVersion(); + break; + + case VERSION_RESP: + pcCommandVersionResp(); + break; + + case WRITE_X: + pcCommandWriteX(); + break; + + case WRITE_W: + pcCommandWriteW(); + break; + + case WRITE_H: + /* treat as normal write to memory, due to hardware limitations */ + pcCommandWriteW(); + break; + + default: + pcCommandUnknown(); + } + } +} + + +/* END */ diff --git a/src/comm.h b/src/comm.h new file mode 100644 index 0000000..05b00f7 --- /dev/null +++ b/src/comm.h @@ -0,0 +1,155 @@ +#ifndef _COMM_H +#define _COMM_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.1.4 (20091212) + * + * NOTE: + * + * Communication with PC using Xtronics ROMulator protocol. + **************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include "global.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* GLOBAL: tells how to treat byte_count in W write command */ +extern BOOL b_bcnt_mode; + +/* GLOBAL: indicates device status */ +extern BOOL b_status_ready; + +/* supported PC commands */ +enum e_command +{ + /* Xtronics ROMulator commands */ + READ_BLOCK = 'R', + READ_BYTE = 'r', + STATUS = 'S', + UNKNOWN = '?', + VERSION = 'V', + WRITE_H = 'w', + WRITE_W = 'W', + WRITE_X = 'X', + + /* extended commands for own usage */ + BYTECOUNT_MODE = 'B', + FIRMWARE = 'F', + MODIFY = 'M', + VERSION_RESP = 'E', /* dirty workaround for ROMulator 'V' command */ +}; + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* counts checksum of given bytes */ +/* ----------------------------------------------------------------------- + RETURN VAL : added all bytes with no carry + ARGS : buffer_ptr - buffer pointer + length - number of bytes to add + * ----------------------------------------------------------------------- */ +BYTE checksum(BYTE* buffer_ptr, const UINT16 length); + +/* process command from PC */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void pcCommand(); + +/* modifies current byte_count handling method */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void pcCommandBytecountMode(); + +/* return version of installed firmware */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void pcCommandFirmware(); + +/* modifies current usart settings */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void pcCommandModify(); + +/* upload to PC block of data */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void pcCommandReadBlock(); + +/* upload to PC single byte of data */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void pcCommandReadByte(); + +/* process command from PC */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + +void pcCommandStatus(); +/* tells whether device is ready to work */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + +void pcCommandUnknown(); +/* illegal command received */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + +void pcCommandVersion(); +/* returns device version */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + +void pcCommandVersionResp(); +/* discards data sent by program as acknowledge of received version */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + +void pcCommandWriteW(); +/* download to uC block of data (used also for hidden-write) */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + +void pcCommandWriteX(); +/* download to uC block of data */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ + + +#endif /* _COMM_H */ +/* END */ diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..273449c --- /dev/null +++ b/src/config.h @@ -0,0 +1,119 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +/************************************************************************** + * PROJECT: Honda RTP rev. 1.0 + * AUTHOR: Marcin O'BenY Benka + * VERSION: 1.0 (20091116) + * + * DESCRIPTION: Simple EEPROM (type 27C256) emulator, compatible with + * Xtronics ROMulator protocol. This device allows to + * perform Real Time Programming of Honda's OBD1 ECU during + * tuning process. + **************************************************************************/ + +/************************************************************************** + * NOTE: + * + * Hardware configuration definitions. + **************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* CPU frequency */ +#define F_CPU 14745600UL + +/* IO */ +/* address bus */ +#define ADDRESS PORTA +#define ADDRESS_DDR DDRA + +/* data bus */ +#define DATA_DDR DDRC +#define DATA_IN PINC +#define DATA_OUT PORTC +#define DATA_PULLUP PORTC + +/* signals mapping */ +#define RAMOE PD2 /* pin 11: ROM outputs enable; low active */ +#define RAMOE_ACT false +#define RAMOE_PORT PORTD + +#define LED_R PD3 /* pin 12: LED1 enable; low active */ +#define LED_R_ACT false +#define LED_R_PORT PORTD + +#define LED_G PD4 /* pin 13: LED2 enable; low active */ +#define LED_G_ACT false +#define LED_G_PORT PORTD + +#define ADRHI PD5 /* pin 14: address hi byte latch enable; low - latched */ +#define ADRHI_ACT false +#define ADRHI_PORT PORTD + +#define ADRLO PD6 /* pin 15: address lo byte latch enable; low - latched */ +#define ADRLO_ACT false +#define ADRLO_PORT PORTD + +#define DATDDIR PD7 /* pin 16: data direction; low - read, high - write */ +#define DATDDIR_READ false +#define DATDDIR_WRITE true +#define DATDDIR_PORT PORTD + +#define RAMWE PB0 /* pin 40: RAM write enable; low active */ +#define RAMWE_ACT false +#define RAMWE_PORT PORTB + +#define ADRSOE PB1 /* pin 41: ECU socket address outputs enable; low active */ +#define ADRSOE_ACT false +#define ADRSOE_PORT PORTB + +#define DATDOE PB2 /* pin 42: uC data outputs enable; low active */ +#define DATDOE_ACT false +#define DATDOE_PORT PORTB + +#define DATSOE PB3 /* pin 43: ECU socket data outputs enable; low active*/ +#define DATSOE_ACT false +#define DATSOE_PORT PORTB + +#define ADRLOE PB4 /* pin 44: uC address latches output enable; low active */ +#define ADRLOE_ACT false +#define ADRLOE_PORT PORTB + +/* USART */ +#define USART_RBUF_SIZE 64 +#define USART_TBUF_SIZE 16 +#define USART_SEND_MAX_LENGTH 262 +#define USART_USE_STATUS +#define USART_USE_TIMER + +/* SYNC TIMER */ +#define SYNC_TIMER_COUNT 9 +#define SYNC_TIMER_PRESCALER 64 +#define SYNC_TIMER_PRESCALER_EDGE +#define SYNC_TIMER_PRESCALER_OCR 255 + +/* MISC */ +#define MEMORY_SIZE 32768 +#define MEMORY_WRITE_RETRIES 3 + +#define FIRMWARE_VERSION FVERSION + +#define ROMULATOR_PROTOCOL_ID 0x31 +#define ROMULATOR_PROTOCOL_MAJOR 0x01 +#define ROMULATOR_PROTOCOL_MINOR 0x28 +#define ROMULATOR_PROTOCOL_WRITE_RETRIES 3 +#define ROMULATOR_PROTOCOL_READ_TIMEOUT 5 + + +#endif /* _CONFIG_H */ +/* END */ diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..0b9c8f8 --- /dev/null +++ b/src/global.h @@ -0,0 +1,72 @@ +#ifndef _GLOBAL_H +#define _GLOBAL_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.4 (20091114) + * + * NOTE: + * + * Globaly used macros and type definitions. + **************************************************************************/ + + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include +#include +#include + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* type definitions */ +typedef uint8_t BYTE; +typedef bool BOOL; + +typedef int8_t INT8; +typedef int16_t INT16; +typedef int32_t INT32; + +typedef uint8_t UINT8; +typedef uint16_t UINT16; +typedef uint32_t UINT32; + + +/*************************************************************************** + * MACROS + ***************************************************************************/ + +/* bit manipulation */ +#define BV(bit) (1 << (bit)) + +/* free pointer and set it to NULL */ +#define FREE(ptr) { if (ptr) { free(ptr); ptr = NULL; } } + +/* convert value to bool */ +#define TO_BOOL(val) (!(!val)) + +/* set given bit of byte to 1 */ +#define sbi(byte, bit) (byte) |= (BV(bit)) + +/* set given bit of byte to 0 */ +#define cbi(byte, bit) (byte) &= ~(BV(bit)) + +/* read bit from value */ +#define GET_BIT(value, bit_n) (TO_BOOL((value) & (BV(bit_n)))) + +/* set bit in value */ +#define SET_BIT(value, bit_n, bool_val) ((bool_val)?(sbi(value, bit_n)):(cbi(value, bit_n))) + +/* change state of bit to opposite in value */ +#define TOGGLE_BIT(value, bit_n) (value ^= BV(bit_n)) + +/* do a short delay using NOP instruction */ +#define NOP_DELAY __asm__ __volatile__("nop"::) + +#endif /* _GLOBAL_H */ +/* END */ diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..51d610c --- /dev/null +++ b/src/io.c @@ -0,0 +1,73 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include + +#include "config.h" +#include "io.h" + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* ----------------------------------------------------------------------- */ +void initIoPorts() +{ + /* set ports direction */ + ADDRESS_DDR = PORT_DIR_OUT; + DATA_DDR = PORT_DIR_OUT; + + /* initialize control pins */ + /* port B */ + SET_BIT(DDRB, RAMWE, PIN_DIR_OUT); + SET_BIT(DDRB, ADRSOE, PIN_DIR_OUT); + SET_BIT(DDRB, DATDOE, PIN_DIR_OUT); + SET_BIT(DDRB, DATSOE, PIN_DIR_OUT); + SET_BIT(DDRB, ADRLOE, PIN_DIR_OUT); + /* port D */ + SET_BIT(DDRD, RAMOE, PIN_DIR_OUT); + SET_BIT(DDRD, LED_R, PIN_DIR_OUT); + SET_BIT(DDRD, LED_G, PIN_DIR_OUT); + SET_BIT(DDRD, ADRHI, PIN_DIR_OUT); + SET_BIT(DDRD, ADRLO, PIN_DIR_OUT); + SET_BIT(DDRD, DATDDIR, PIN_DIR_OUT); + + /* enable leds */ + SET_BIT(LED_R_PORT, LED_R, LED_R_ACT); + SET_BIT(LED_G_PORT, LED_G, LED_G_ACT); + + /* set default outputs state - deny any operation to SRAM */ + ADDRESS = 0x00; + DATA_OUT = 0x00; + + SET_BIT(RAMOE_PORT, RAMOE, !RAMOE_ACT); + SET_BIT(RAMWE_PORT, RAMWE, !RAMWE_ACT); + + SET_BIT(ADRHI_PORT, ADRHI, !ADRHI_ACT); + SET_BIT(ADRLO_PORT, ADRLO, !ADRLO_ACT); + + SET_BIT(ADRLOE_PORT, ADRLOE, !ADRLOE_ACT); + SET_BIT(ADRSOE_PORT, ADRSOE, !ADRSOE_ACT); + + SET_BIT(DATSOE_PORT, DATSOE, !DATSOE_ACT); + SET_BIT(DATDOE_PORT, DATDOE, !DATDOE_ACT); + + SET_BIT(DATDDIR_PORT, DATDDIR, DATDDIR_READ); + + /* prevent from activating pull-ups */ + sbi(SFIOR, PUD); +} + + +/* END */ diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..ed63f08 --- /dev/null +++ b/src/io.h @@ -0,0 +1,48 @@ +#ifndef _IO_H +#define _IO_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.4 (20091116) + * + * NOTE: + * + * Collection of basic I/O routines and definitions. + **************************************************************************/ + + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include "global.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +#define PORT_DIR_IN 0x00 +#define PORT_DIR_OUT 0xFF +#define PORT_PULLUP_ACT 0xFF + +#define PIN_DIR_IN false +#define PIN_DIR_OUT true +#define PIN_PULLUP_ACT true +#define PIN_PULLUP_NACT false + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* initialization of I/O ports */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void initIoPorts(); + + +#endif /* _IO_H */ +/* END */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..b42e100 --- /dev/null +++ b/src/main.c @@ -0,0 +1,134 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include "config.h" +#include "comm.h" +#include "io.h" +#include "main.h" +#include "memory.h" +#include "sync_timer.h" +#include "usart.h" + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* ----------------------------------------------------------------------- */ +int main() +{ + UINT8 ui8_sync_timer_10 = 0; + UINT8 ui8_sync_timer_100 = 0; + + /* do initialization - both leds light*/ + init(); + + /* test memory and exit if write operation failed */ + if (!memoryTest()) + { + SET_BIT(LED_G_PORT, LED_G, !LED_G_ACT); + + return (0); + } + + /* set default state of all cells to 0xFF */ + if (!memoryClear()) + { + SET_BIT(LED_G_PORT, LED_G, !LED_G_ACT); + + return (0); + } + + /* if tests passed ok then, only green led blinks */ + SET_BIT(LED_R_PORT, LED_R, !LED_R_ACT); + b_status_ready = true; + + /* set default state to: read by ECU available */ + setMemoryAccessEcuRead(); + + /* start infinite loop */ + while (1) + { + /* code execuded 100 times a second */ + if (sync()) + { + exec100(); + + ui8_sync_timer_10++; + + /* code executed 10 times a second */ + if (ui8_sync_timer_10 == 10) + { + exec10(); + + ui8_sync_timer_10 = 0; + ui8_sync_timer_100++; + + /* code executed once a second */ + if (ui8_sync_timer_100 == 10) + { + exec1(); + + ui8_sync_timer_100 = 0; + } + } + } + + /* code executed in each pass */ + exec(); + } +} + +/* ----------------------------------------------------------------------- */ +void init() +{ + /* initialize peripherials */ + initIoPorts(); + initSyncTimer(); + + /* default settings supported by CROME 1.5.3 and above */ + initUsart(USART_BAUD_920000, false, true, USART_PARITY_EVEN); + + /* enable interrupts */ + sei(); +} + + +/* ----------------------------------------------------------------------- */ +void exec() +{ + if (usartUnreadBytes()) + pcCommand(); + + setMemoryAccessEcuRead(); + b_status_ready = true; +} + +/* ----------------------------------------------------------------------- */ +void exec1() +{ + TOGGLE_BIT(LED_G_PORT, LED_G); +} + +/* ----------------------------------------------------------------------- */ +void exec10() +{ +} + +/* ----------------------------------------------------------------------- */ +void exec100() +{ +} + + +/* END */ diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..c697e01 --- /dev/null +++ b/src/main.h @@ -0,0 +1,54 @@ +#ifndef _MAIN_H +#define _MAIN_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.0 (20091116) + * + * NOTE: + * + * Program entry point and related functions. + **************************************************************************/ + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* initialize microcontroller */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void init(); + +/* executed on each mail loop pass */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void exec(); + +/* executed once a second */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void exec1(); + +/* executed each 1/10 of second */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void exec10(); + +/* executed each 1/100 of second */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void exec100(); + + +#endif /* _COMM_H */ +/* END */ diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..a061d2a --- /dev/null +++ b/src/memory.c @@ -0,0 +1,295 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include + +#include "config.h" +#include "global.h" +#include "io.h" +#include "memory.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* GLOBAL: data send/receive buffer for communication with PC */ +BYTE data_buffer[262]; + +/* indicates whether device is accessible for ECU */ +static BOOL b_ecu_read_avail = false; + +/* data byte used commonly in read/write and test operations */ +static BYTE data_byte; + +/* indexing variable for memory operations */ +static UINT16 index = 0; + + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* ----------------------------------------------------------------------- */ +BOOL memoryClear() +{ + /* default state for eeproms is 0xFF for each byte */ + data_byte = 0xFF; + + for (index = 0; index < MEMORY_SIZE; ++index) + { + if (!memoryWrite(index, data_byte)) + return (false); + } + + return (true); +} + +/* ----------------------------------------------------------------------- */ +BOOL memoryTest() +{ + /* patterns testing */ + data_byte = 0x00; + + /* TEST1: 0x00 pattern */ + for (index = 0; index < MEMORY_SIZE; ++index) + memoryWrite(index, data_byte); + + for (index = 0; index < MEMORY_SIZE; ++index) + { + if (*(memoryRead(index)) != 0x00) + return (false); + } + + /* TEST2: 0x55 odd and 0xAA even pattern */ + for (index = 0; index < MEMORY_SIZE; ++index) + { + if ((index & 1) == 0) + data_byte = 0x55; + else + data_byte = 0xAA; + + if (!memoryWrite(index, data_byte)) + return (false); + } + + /* read from memory for verification */ + for (index = 0; index < MEMORY_SIZE; ++index) + { + data_byte = *(memoryRead(index)); + + if ((index & 1) == 0) + { + if (data_byte != 0x55) + return (false); + } + else + { + if (data_byte != 0xAA) + return (false); + } + } + + /* TEST3: 0xAA odd and 0x55 even pattern */ + for (index = 0; index < MEMORY_SIZE; ++index) + { + if ((index & 1) == 0) + data_byte = 0xAA; + else + data_byte = 0x55; + + if (!memoryWrite(index, data_byte)) + return (false); + } + + /* read from memory for verification */ + for (index = 0; index < MEMORY_SIZE; ++index) + { + data_byte = *(memoryRead(index)); + + if ((index & 1) == 0) + { + if (data_byte != 0xAA) + return (false); + } + else + { + if (data_byte != 0x55) + return (false); + } + } + + return (true); +} + +/* ----------------------------------------------------------------------- */ +BOOL memoryWrite(const UINT16 addr, const BYTE byte) +{ + static BYTE vrfy_byte; + + for (UINT8 retry = 0; retry < MEMORY_WRITE_RETRIES; ++retry) + { + setMemoryAccessMcuWrite(); + setMemoryAddress(addr); + + DATA_OUT = byte; + + /* write to memory */ + SET_BIT(RAMWE_PORT, RAMWE, RAMWE_ACT); + NOP_DELAY; + SET_BIT(RAMWE_PORT, RAMWE, !RAMWE_ACT); + + /* read byte from memory and check wheter written byte match */ + vrfy_byte = *(memoryRead(addr)); + + if (byte == vrfy_byte) + return (true); + } + + /* if something went bad, light red led */ + SET_BIT(LED_R_PORT, LED_R, LED_R_ACT); + + return (false); +} + +/* ----------------------------------------------------------------------- */ +BOOL memoryWriteBlock(const UINT16 addr, const UINT16 size, const BYTE* buffer) +{ + for (index = 0; index < size; ++index) + { + if (!memoryWrite((addr + index), *(buffer + index))) + return (false); + } + + return (true); +} + +/* ----------------------------------------------------------------------- */ +BYTE* memoryRead(const UINT16 addr) +{ + static BYTE byte; + + setMemoryAccessMcuRead(); + setMemoryAddress(addr); + + byte = DATA_IN; + + return (&byte); +} + +/* ----------------------------------------------------------------------- */ +BYTE* memoryReadBlock(const UINT16 addr, const UINT16 size, BYTE* buffer) +{ + for (index = 0; index < size; ++index) + *(buffer + index) = *(memoryRead(addr + index)); + + return (buffer); +} + +/* ----------------------------------------------------------------------- */ +void setMemoryAccessEcuRead() +{ + if (b_ecu_read_avail) + return; + + SET_BIT(RAMOE_PORT, RAMOE, RAMOE_ACT); + SET_BIT(RAMWE_PORT, RAMWE, !RAMWE_ACT); + + SET_BIT(ADRHI_PORT, ADRHI, !ADRHI_ACT); + SET_BIT(ADRLO_PORT, ADRLO, !ADRLO_ACT); + + SET_BIT(ADRLOE_PORT, ADRLOE, !ADRLOE_ACT); + SET_BIT(ADRSOE_PORT, ADRSOE, ADRSOE_ACT); + + SET_BIT(DATSOE_PORT, DATSOE, DATSOE_ACT); + SET_BIT(DATDOE_PORT, DATDOE, !DATDOE_ACT); + + SET_BIT(DATDDIR_PORT, DATDDIR, DATDDIR_READ); + + b_ecu_read_avail = true; +} + +/* ----------------------------------------------------------------------- */ +void setMemoryAccessMcuRead() +{ + b_ecu_read_avail = false; + + SET_BIT(RAMOE_PORT, RAMOE, RAMOE_ACT); + SET_BIT(RAMWE_PORT, RAMWE, !RAMWE_ACT); + + SET_BIT(ADRHI_PORT, ADRHI, !ADRHI_ACT); + SET_BIT(ADRLO_PORT, ADRLO, !ADRLO_ACT); + + SET_BIT(ADRLOE_PORT, ADRLOE, ADRLOE_ACT); + SET_BIT(ADRSOE_PORT, ADRSOE, !ADRSOE_ACT); + + SET_BIT(DATSOE_PORT, DATSOE, !DATSOE_ACT); + SET_BIT(DATDOE_PORT, DATDOE, DATDOE_ACT); + + SET_BIT(DATDDIR_PORT, DATDDIR, DATDDIR_READ); + + DATA_DDR = PORT_DIR_IN; + + /* ensure that pull-ups are disabled */ + DATA_PULLUP = (BYTE)~PORT_PULLUP_ACT; + + /* wait for data propagation */ + NOP_DELAY; +} + +/* ----------------------------------------------------------------------- */ +void setMemoryAccessMcuWrite() +{ + b_ecu_read_avail = false; + + SET_BIT(RAMOE_PORT, RAMOE, !RAMOE_ACT); + SET_BIT(RAMWE_PORT, RAMWE, !RAMWE_ACT); + + SET_BIT(ADRHI_PORT, ADRHI, !ADRHI_ACT); + SET_BIT(ADRLO_PORT, ADRLO, !ADRLO_ACT); + + SET_BIT(ADRLOE_PORT, ADRLOE, ADRLOE_ACT); + SET_BIT(ADRSOE_PORT, ADRSOE, !ADRSOE_ACT); + + SET_BIT(DATSOE_PORT, DATSOE, !DATSOE_ACT); + SET_BIT(DATDOE_PORT, DATDOE, DATDOE_ACT); + + SET_BIT(DATDDIR_PORT, DATDDIR, DATDDIR_WRITE); + + DATA_DDR = PORT_DIR_OUT; + + /* wait for data propagation */ + NOP_DELAY; +} + +/* ----------------------------------------------------------------------- */ +void setMemoryAddress(const UINT16 addr) +{ + /* set lo byte of address and wait for propagation before latching */ + ADDRESS = addr; + SET_BIT(ADRLO_PORT, ADRLO, !ADRLO_ACT); + NOP_DELAY; + SET_BIT(ADRLO_PORT, ADRLO, ADRLO_ACT); + NOP_DELAY; + + /* the same with hi byte of address */ + ADDRESS = (addr >> 8); + SET_BIT(ADRHI_PORT, ADRHI, !ADRHI_ACT); + NOP_DELAY; + SET_BIT(ADRHI_PORT, ADRHI, ADRHI_ACT); + NOP_DELAY; +} + + +/* END */ diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..ba1bfc6 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,109 @@ +#ifndef _MEMORY_H +#define _MEMORY_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.1 (20091124) + * + * NOTE: + * + * Operations on volatile SRAM memory. + **************************************************************************/ + + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include "global.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* GLOBAL: data send/receive buffer for communication with PC */ +extern BYTE data_buffer[262]; + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* clear whole memory, writting 0x00 to each cell */ +/* ----------------------------------------------------------------------- + RETURN VAL : TRUE on success, FALSE on fail + ARGS : NONE + * ----------------------------------------------------------------------- */ +BOOL memoryClear(); + +/* test SRAM memory, writting different patterns */ +/* ----------------------------------------------------------------------- + RETURN VAL : TRUE on success, FALSE on fail + ARGS : NONE + * ----------------------------------------------------------------------- */ +BOOL memoryTest(); + +/* write byte to memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : TRUE on success, FALSE on fail + ARGS : addr - address in memory matrix + byte - byte to write to memory + * ----------------------------------------------------------------------- */ +BOOL memoryWrite(const UINT16 addr, const BYTE byte); + +/* write block of data to memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : TRUE on success, FALSE on fail + ARGS : addr - address of first byte in memory matrix + size - bytes number to write to memory + buffer - pointer to data buffer with data to write + * ----------------------------------------------------------------------- */ +BOOL memoryWriteBlock(const UINT16 addr, const UINT16 size, const BYTE* buffer); + +/* read byte from memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : pointer to byte read + ARGS : addr - address in memory matrix + * ----------------------------------------------------------------------- */ +BYTE* memoryRead(const UINT16 addr); + +/* read block of data from memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : pointer to data read + ARGS : addr - address of first byte in memory matrix + size - bytes number to read from memory + buffer - pointer to buffer where data will be written + * ----------------------------------------------------------------------- */ +BYTE* memoryReadBlock(const UINT16 addr, const UINT16 size, BYTE* buffer); + +/* enable ECU to read from memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void setMemoryAccessEcuRead(); + +/* enable MCU to read from memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void setMemoryAccessMcuRead(); + +/* enable MCU to write to memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void setMemoryAccessMcuWrite(); + +/* set memory address */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void setMemoryAddress(const UINT16 addr); + + +#endif /* _MEMORY_H */ +/* END */ diff --git a/src/sync_timer.c b/src/sync_timer.c new file mode 100644 index 0000000..377862f --- /dev/null +++ b/src/sync_timer.c @@ -0,0 +1,125 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include "config.h" +#include "sync_timer.h" +#include "usart.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* overflow counter */ +static volatile UINT8 ui8_sync_timer_overflow = 0; + +/* flag that is set 100 per second */ +static volatile BOOL b_100 = false; + +/* TIMER0 compare interrupt handler */ +ISR(TIMER0_COMP_vect) +{ + ++ui8_sync_timer_overflow; + + if (ui8_sync_timer_overflow == SYNC_TIMER_COUNT) + { + ui8_sync_timer_overflow = 0; + b_100 = true; + +#ifdef USART_USE_TIMER + /* used by usartRead for timeout */ + ++usart_timeout; +#endif /* USART USE_TIMER */ + } +} + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* ----------------------------------------------------------------------- */ +inline BOOL sync() +{ + if (b_100) + { + b_100 = false; + + return (true); + } + + return (false); +} + +/* ----------------------------------------------------------------------- */ +void initSyncTimer() +{ + /* set counter0 value */ + OCR0 = SYNC_TIMER_PRESCALER_OCR; + + /* enable counter0 interrupt */ + TIMSK = BV(OCIE0); + + /* enable counter0 compare */ + TCCR0 = BV(WGM01); + +#ifdef SYNC_TIMER_PRESCALER_EDGE + /* prescaler <= fcpu/1 */ + #if SYNC_TIMER_PRESCALER == 1 + TCCR0 |= BV(CS00); + /* prescaler <= fcpu/8 */ + #elif SYNC_TIMER_PRESCALER == 8 + TCCR0 |= BV(CS01); + /* prescaler <= fcpu/64 */ + #elif SYNC_TIMER_PRESCALER == 64 + TCCR0 |= BV(CS00) | BV(CS01); + /* prescaler <= fcpu/256 */ + #elif SYNC_TIMER_PRESCALER == 256 + TCCR0 |= BV(CS02); + /* prescaler <= fcpu/1024 */ + #elif SYNC_TIMER_PRESCALER == 1024 + TCCR0 |= BV(CS00) | BV(CS02); + #else + #error "SYNC_TIMER: SYNC_TIMER_PRESCALER not set" + #endif /* SYNC_TIMER_PRESCALER */ +#else + /* prescaler <= fcpu/1 */ + #if SYNC_TIMER_PRESCALER == 1 + TCCR0 |= BV(CS00); + /* prescaler <= fcpu/8 */ + #elif SYNC_TIMER_PRESCALER == 8 + TCCR0 |= BV(CS01); + /* prescaler <= fcpu/32 */ + #elif SYNC_TIMER_PRESCALER == 32 + TCCR0 |= BV(CS00) | BV(CS01); + /* prescaler <= fcpu/64 */ + #elif SYNC_TIMER_PRESCALER == 64 + TCCR0 |= BV(CS02); + /* prescaler <= fcpu/128 */ + #elif SYNC_TIMER_PRESCALER == 128 + TCCR0 |= BV(CS00) | BV(CS02); + /* prescaler <= fcpu/8 */ + #elif SYNC_TIMER_PRESCALER == 256 + TCCR0 |= BV(CS01) | BV(CS02); + /* prescaler <= fcpu/8 */ + #elif SYNC_TIMER_PRESCALER == 1024 + TCCR0 |= BV(CS00) | BV(CS01) | BV(CS02); + /* prescaler not set */ + #else + #error "SYNC_TIMER: SYNC_TIMER_PRESCALER not set" + #endif /* SYNC_TIMER_PRESCALER */ +#endif /* SYNC_TIMER_PRESCALER_EDGE */ +} + + +/* END */ diff --git a/src/sync_timer.h b/src/sync_timer.h new file mode 100644 index 0000000..6f41fa9 --- /dev/null +++ b/src/sync_timer.h @@ -0,0 +1,55 @@ +#ifndef _SYNC_TIMER_H +#define _SYNC_TIMER_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.5 (20100225) + * + * NOTE: + * + * Timer for main event loop synchronization. + * sync ticks presents following equotation: + * sync = fcpu/(SYNC_TIMER_PRESCALER*SYNC_TIMER_PRESCALER_OCR*SYNC_TIMER_COUNT) + * sync value should give execution each 1/100 of second. + * + * IMPORTANT: + * This library need to set following constants to work: + * - SYNC_TIMER_COUNT - additional divider for reaching desired sync times + * - SYNC_TIMER_PRESCALER - value to divide fcpu signal for timer + * supported values: 1, 8, 32, 64, 128, 256 and 1024 + * - SYNC_TIMER_PRESCALER_OCR - max value for counter + * - SYNC_TIMER_PRESCALER_EDGE - set it for MCUs that sets + * edge reaction via CSXX bits. + **************************************************************************/ + + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include + +#include "global.h" + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* tells whether 1/100 of second expired */ +/* ----------------------------------------------------------------------- + RETURN VAL : TRUE if 10ms from previous execution elapsed, FALSE otherwise + ARGS : NONE + * ----------------------------------------------------------------------- */ +BOOL sync(); + +/* initialization of timer */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void initSyncTimer(); + + +#endif /* _SYNC_TIMER_H */ +/* END */ diff --git a/src/usart.c b/src/usart.c new file mode 100644 index 0000000..713c76f --- /dev/null +++ b/src/usart.c @@ -0,0 +1,366 @@ +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include +#include +#include +#include + +#include "usart.h" + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* check for receive/transmit buffer size */ +#if (USART_RBUF_SIZE >= 0xFF | USART_TBUF_SIZE >= 0xFF) + #error "USART: buffers too big (max = 256 bytes)" +#endif + +#if (USART_RBUF_SIZE & USART_RMASK) + #error "USART: RX buffer size is not power of 2" +#endif + +#if (USART_TBUF_SIZE & USART_TMASK) + #error "USART: TX buffer size is not power of 2" +#endif + +/* transmission buffers */ +static volatile BYTE rx_buff[USART_RBUF_SIZE]; +static volatile BYTE tx_buff[USART_TBUF_SIZE]; + +/* buffer indexes */ +static volatile UINT8 rx_head, rx_tail; +static volatile UINT8 tx_head, tx_tail; + +/* usart status */ +static volatile e_usartstatus_t usartstatus; + +#ifdef USART_USE_TIMER +/* timeout counter for read timeout */ +volatile UINT16 usart_timeout = 0; +#endif /* USART_USE_TIMER */ + +/* incoming byte interrupt handler */ +ISR (USART_INT_RX) +{ +#ifdef USART_USE_STATUS + /* set status of received byte */ + if (USART_UCSRA & BV(FE)) + sbi(usartstatus, RX_STAT_FE); + if (USART_UCSRA & BV(DOR)) + sbi(usartstatus, RX_STAT_DOR); +/* Atmegas have different name of this flag */ +#ifdef UPE + if (USART_UCSRA & BV(UPE)) + sbi(usartstatus, RX_STAT_PE); +#else + if (USART_UCSRA & BV(PE)) + sbi(usartstatus, RX_STAT_PE); +#endif /* UPE */ +#endif /* USART_USE_STATUS */ + + /* discard last byte in buffer if buffer overflow occured */ + if (usartstatus & RX_OVR) + rx_tail = (rx_tail + 1) & USART_RMASK; + + /* insert byte to ring buffer */ + rx_buff[rx_head] = USART_UDR; + rx_head = (rx_head + 1) & USART_RMASK; + + /* indicate buffer overflow */ + if (rx_head == rx_tail) + sbi(usartstatus, RX_STAT_OVR); +} + +/* outgoing byte interrupt handler */ +ISR (USART_INT_UDRE) +{ + if ((tx_tail != tx_head) || (usartstatus & TX_FULL)) + { + USART_UDR = tx_buff[tx_tail]; + tx_tail = (tx_tail + 1) & USART_TMASK; + + cbi(usartstatus, TX_STAT_FULL); + } + else + cbi(USART_UCSRB, USART_UDRIE); +} + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* ----------------------------------------------------------------------- */ +BOOL usartReceiveBufferOverflow() +{ + return (TO_BOOL((usartstatus & RX_OVR))); +} + +#ifndef USART_USE_TIMER +/* ----------------------------------------------------------------------- */ +BOOL usartRead(BYTE *str, const UINT16 count) +{ +#ifdef USART_USE_STATUS + static BOOL b_byte_read_ok; +#endif /* USART_USE_STATUS */ + + for (UINT16 i = 0; i < count; ++i) + { + /* if any timeout is present, wait for incomming byte */ + while (!usartUnreadBytes()); + +#ifdef USART_USE_STATUS + *str = usartReadByte(&b_byte_read_ok); +#endif /* USART_USE_STATUS */ + ++str; + +#ifdef USART_USE_STATUS + if (!b_byte_read_ok) + return (false); +#endif /* USART_USE_STATUS */ + } + + return (true); +} + +#else +/* ----------------------------------------------------------------------- */ +BOOL usartRead(BYTE *str, const UINT16 count, const UINT16 timeout) +{ +#ifdef USART_USE_STATUS + static BOOL b_byte_read_ok; +#endif /* USART_USE_STATUS */ + + for (UINT16 i = 0; i < count; ++i) + { + usart_timeout = 0; + + /* if any timeout is present, wait for incomming byte */ + while (!usartUnreadBytes() && timeout) + { + if (usart_timeout == timeout) + return (false); + } + +#ifdef USART_USE_STATUS + *str = usartReadByte(&b_byte_read_ok); +#endif /* USART_USE_STATUS */ + ++str; +#ifdef USART_USE_STATUS + + if (!b_byte_read_ok) + return (false); +#endif /* USART_USE_STATUS */ + } + + return (true); +} +#endif /* USART_USE_TIMER */ + +/* ----------------------------------------------------------------------- */ +#ifdef USART_USE_STATUS +BYTE usartReadByte(BOOL *ok) +#else +BYTE usartReadByte() +#endif /* USART_USE_STATUS */ +{ + static BYTE b; + + if (usartUnreadBytes()) + { + b = rx_buff[rx_tail]; + rx_tail = (rx_tail + 1) & USART_RMASK; + cbi(usartstatus, RX_STAT_OVR); + +#ifdef USART_USE_STATUS + if (ok) + { + if (usartstatus & (RX_FE | RX_PE | RX_DOR)) + { + *ok = false; + + /* clear error flags */ + cbi(usartstatus, RX_STAT_FE); + cbi(usartstatus, RX_STAT_PE); + cbi(usartstatus, RX_STAT_DOR); + } + else + *ok = true; + } + + return (b); + } + else + { + if (ok) + *ok = false; + + return (0); + } +#else + } + return (b); +#endif /* USART_USE_STATUS */ +} + +/* ----------------------------------------------------------------------- */ +UINT8 usartUnreadBytes() +{ + if (usartstatus & RX_OVR) + return (USART_RBUF_SIZE); + if (rx_head > rx_tail) + return (rx_head - rx_tail); + if (rx_head < rx_tail) + return (USART_RBUF_SIZE - (rx_tail - rx_head)); + return (0); +} + +/* ----------------------------------------------------------------------- */ +void initUsart(const UINT16 baudrate, const BOOL double_speed, const BOOL two_stop_bits, const UINT8 parity) +{ + UINT16 usart_const; + UINT8 ucsrc; + + float f_usart_const; + + rx_head = 0; + rx_tail = 0; + tx_head = 0; + tx_tail = 0; + usartstatus = 0; + + /* set speed */ + if (double_speed) + { + /* compute USART clock and round to nearest value */ + f_usart_const = (float)(USART_F_CPU) / (float)(8); + f_usart_const /= (float)(baudrate); + f_usart_const -= 1; + + usart_const = (UINT16)(lrint(f_usart_const)); + USART_UCSRA = BV(UDRE) | BV(U2X); + } + else + { + /* compute USART clock and round to nearest value */ + f_usart_const = (float)(USART_F_CPU) / (float)(16); + f_usart_const /= (float)(baudrate); + f_usart_const -= 1; + + usart_const = (UINT16)(lrint(f_usart_const)); + USART_UCSRA = BV(UDRE); + } + USART_UBRRH = (UINT8)(usart_const >> 8); + USART_UBRRL = (UINT8)usart_const; + + /* set rx and tx enabled, allow interrupt on new byte received */ + USART_UCSRB = BV(RXEN) | BV(TXEN) | BV(RXCIE); + + /* set frame format: asynchronous, 8bit data */ + ucsrc = BV(UCSZ0) | BV(UCSZ1); + + /* set frame format: 2 stop bits */ + if (two_stop_bits) + ucsrc |= BV(USBS); + + /* set frame format: parity */ + if (parity == USART_PARITY_EVEN) + ucsrc |= BV(UPM1); + else if (parity == USART_PARITY_ODD) + ucsrc |= BV(UPM0) | BV(UPM1); + + /* URSEL for access to UCSRC insetad of UBRRH */ +#ifdef URSEL + ucsrc |= BV(URSEL); +#endif /* URSEL */ + + USART_UCSRC = ucsrc; +} + +/* ----------------------------------------------------------------------- */ +void usartFlush() +{ + rx_tail = rx_head; + cbi(usartstatus, RX_STAT_OVR); +} + +/* ----------------------------------------------------------------------- */ +void usartSend(BYTE *str, const UINT16 length) +{ + static UINT16 real_length; + + /* compute max length of data to send */ + if (!length) + real_length = USART_SEND_MAX_LENGTH; + else + real_length = (length > USART_SEND_MAX_LENGTH)?(USART_SEND_MAX_LENGTH):(length); + + /* start sending data */ + for (UINT16 i = 0; i < real_length; ++i) + { + usartSendByte(*str); + ++str; + } +} + +/* ----------------------------------------------------------------------- */ +void usartSendByte(const BYTE b) +{ + /* wait for free space in buffer */ + while (usartstatus & TX_FULL); + + /* insert byte to transmit buffer */ + tx_buff[tx_head] = b; + tx_head = (tx_head + 1) & USART_TMASK; + + if (tx_head == tx_tail) + sbi(usartstatus, TX_STAT_FULL); + + sbi(USART_UCSRB, USART_UDRIE); +} + +/* ----------------------------------------------------------------------- */ +void usartSendString(BYTE *str, const UINT16 length) +{ + static UINT16 real_length; + + /* compute max length of data to send */ + if (!length) + real_length = USART_SEND_MAX_LENGTH; + else + real_length = (length > USART_SEND_MAX_LENGTH)?(USART_SEND_MAX_LENGTH):(length); + + /* start sending data */ + for (UINT16 i = 0; (i < real_length) && *str; ++i) + { + usartSendByte(*str); + ++str; + } +} + +/* ----------------------------------------------------------------------- */ +void usartSendString_P(const BYTE *str) +{ + BYTE c; + + while ((c = pgm_read_byte(str))) + { + usartSendByte(c); + ++str; + } +} + + +/* END */ diff --git a/src/usart.h b/src/usart.h new file mode 100644 index 0000000..881798a --- /dev/null +++ b/src/usart.h @@ -0,0 +1,269 @@ +#ifndef _USART_H +#define _USART_H + +/************************************************************************** + * Written by Marcin O'BenY Benka + * Version 1.11 (20100224) + * + * NOTE: + * + * Buffered USART support. + * + * IMPORTANT: + * This library needs to set following constants to work: + * - F_CPU - cpu frequency + * - USART_RBUF_SIZE - receive buffer size (only power of 2) + * - USART_TBUF_SIZE - transmit buffer size (only power of 2) + * - USART_SEND_MAX_LENGTH - max length of transmitted data block + * - USART_USE_PORT1 - uses USART1 instead of USART0 + * - USART_USE_STATUS - uses status register for received data + * - USART_USE_TIMER - enables support for timer-based read timeout + **************************************************************************/ + +/*************************************************************************** + * INCLUDES + ***************************************************************************/ + +#include "config.h" +#include "global.h" + + +/*************************************************************************** + * DEFINITIONS + ***************************************************************************/ + +/* configure first USART port */ +#ifndef USART_USE_PORT1 + /* for one USART port MCUs*/ + #ifdef UCSRA + #define USART_UBRRH UBRRH + #define USART_UBRRL UBRRL + + #define USART_UCSRA UCSRA + #define USART_UCSRB UCSRB + #define USART_UCSRC UCSRC + + #define USART_UDR UDR + #define USART_UDRIE UDRIE + + /* some Atmegas has got USART_RX_vect others USART_RXC_vect defined */ + #ifdef USART_RX_vect + #define USART_INT_RX USART_RX_vect + #else + #define USART_INT_RX USART_RXC_vect + #endif /* USART_RX_vect */ + #define USART_INT_UDRE USART_UDRE_vect + /* for two USART ports MCUs*/ + #else + #define USART_UBRRH UBRR0H + #define USART_UBRRL UBRR0L + + #define USART_UCSRA UCSR0A + #define USART_UCSRB UCSR0B + #define USART_UCSRC UCSR0C + + #define USART_UDR UDR0 + #define USART_UDRIE UDRIE0 + + /* some Atmegas has got USART0_RX_vect others USART0_RXC_vect defined */ + #ifdef USART0_RX_vect + #define USART_INT_RX USART0_RX_vect + #else + #define USART_INT_RX USART0_RXC_vect + #endif /* USART_RX_vect */ + #define USART_INT_UDRE USART0_UDRE_vect + #endif /* UCSRA */ +/* configure second USART port */ +#else + #define USART_UBRRH UBRR1H + #define USART_UBRRL UBRR1L + + #define USART_UCSRA UCSR1A + #define USART_UCSRB UCSR1B + #define USART_UCSRC UCSR1C + + #define USART_UDR UDR1 + #define USART_UDRIE UDRIE1 + + /* some Atmegas has got USART1_RX_vect others USART1_RXC_vect defined */ + #ifdef USART1_RX_vect + #define USART_INT_RX USART1_RX_vect + #else + #define USART_INT_RX USART1_RXC_vect + #endif /* USART_RX_vect */ + #define USART_INT_UDRE USART1_UDRE_vect +#endif /* USART_USE_PORT1 */ + +/* cpu frequency divided by 100 used for initialization */ +#define USART_F_CPU (F_CPU/100) + +/* buffers masks */ +/* NOTE: Works only for values that are power of 2. This is used for fast + * modulo in circular transmit/receive buffer. + */ +#define USART_RMASK (USART_RBUF_SIZE-1) +#define USART_TMASK (USART_TBUF_SIZE-1) + +/* status bits */ +#define RX_STAT_DOR 3 +#define RX_STAT_FE 4 +#define RX_STAT_PE 2 +#define RX_STAT_OVR 1 +#define TX_STAT_FULL 0 + +/* usart baud rates */ +enum e_usartbaud +{ + /* standard baudrates */ + USART_BAUD_9600 = 96, + USART_BAUD_19200 = 192, + USART_BAUD_38400 = 384, + USART_BAUD_57600 = 576, + USART_BAUD_115200 = 1152, + USART_BAUD_230400 = 2304, + USART_BAUD_460800 = 4608, + USART_BAUD_921600 = 9216, + + /* custom baudrates */ + USART_BAUD_460000 = 4600, + USART_BAUD_920000 = 9200 +}; +typedef enum e_usartbaud e_usartbaud_t; + +/* usart parity settings */ +enum e_usartparity +{ + USART_PARITY_NONE = 0, + USART_PARITY_EVEN = 1, + USART_PARITY_ODD = 2 +}; +typedef enum e_usartparity e_usartparity_t; + +/* usart status */ +enum e_usartstatus +{ + RX_DOR = BV(RX_STAT_DOR), /* rx dataoverrun */ + RX_FE = BV(RX_STAT_FE), /* rx frame error */ + RX_PE = BV(RX_STAT_PE), /* rx parity error */ + RX_OVR = BV(RX_STAT_OVR), /* rx ring buffer overflow */ + TX_FULL = BV(TX_STAT_FULL) /* tx full buffer */ +}; +typedef enum e_usartstatus e_usartstatus_t; + +#ifdef USART_USE_TIMER +/* timeout counter (incremented in sync_timer) */ +extern volatile UINT16 usart_timeout; +#endif /* USART_USE_TIMER */ + + +/*************************************************************************** + * FUNCTIONS + ***************************************************************************/ + +/* read bytes and write them to buffer */ +/* ----------------------------------------------------------------------- + RETURN VAL : TRUE if receive buffer overflow occured, FALSE otherwise + ARGS : NONE + * ----------------------------------------------------------------------- */ +BOOL usartReceiveBufferOverflow(); + +#ifndef USART_USE_TIMER +/* read bytes and write them to buffer */ +/* ----------------------------------------------------------------------- + RETURN VAL : pointer to buffer where data was written + ARGS : str - pointer to buffer, where data will be written + count - bytes to read + NOTE : if USART_USE_TIMER isn't declared, function won't + have timeout support + * ----------------------------------------------------------------------- */ +BOOL usartRead(BYTE* str, const UINT16 count); + +#else +/* read bytes and write them to buffer */ +/* ----------------------------------------------------------------------- + RETURN VAL : pointer to buffer where data was written + ARGS : str - pointer to buffer, where data will be written + count - bytes to read + timeout - timeout in 1/100 of second (less then 65536) + NOTE : if USART_USE_TIMER is declared, function will have + timer-based timeout support enabled (see sync_timer.h) + * ----------------------------------------------------------------------- */ +BOOL usartRead(BYTE* str, const UINT16 count, const UINT16 timeout); +#endif /* USART_USE_TIMER */ + +#ifdef USART_USE_STATUS +/* read next byte in buffer, be carefull for buffer overflow */ +/* ----------------------------------------------------------------------- + RETURN VAL : read byte or 0 if buffer is empty + ARGS : ok - TRUE on byte read, FALSE on fail + * ----------------------------------------------------------------------- */ +BYTE usartReadByte(BOOL *ok); + +#else +/* read next byte in buffer, be carefull for buffer overflow */ +/* ----------------------------------------------------------------------- + RETURN VAL : read byte or 0 if buffer is empty + ARGS : NONE + * ----------------------------------------------------------------------- */ +BYTE usartReadByte(); +#endif /* USART_USE_STATUS */ + +/* unread bytes count */ +/* ----------------------------------------------------------------------- + RETURN VAL : number of unread bytes in buffer + ARGS : NONE + * ----------------------------------------------------------------------- */ +UINT8 usartUnreadBytes(); + +/* initialization of USART */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : baudrate - baudrate divided by 100 or value from e_usartbaud + double_speed - enables usart double speed mode + two_stop_bits - use two stop bits in frame + parity - use parity checking - e_usartparity + * ----------------------------------------------------------------------- */ +void initUsart(const UINT16 baudrate, const BOOL double_speed, const BOOL two_stop_bits, const UINT8 parity); + +/* flush bytes waiting in receive buffer (ignore them) */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : NONE + * ----------------------------------------------------------------------- */ +void usartFlush(); + +/* send data */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : str - string to be sent + length - max length of given string + (limited to USART_SEND_MAX_LENGTH) + * ----------------------------------------------------------------------- */ +void usartSend(BYTE *str, const UINT16 length); + +/* send single byte */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : b - byte to be transmitted + * ----------------------------------------------------------------------- */ +void usartSendByte(const BYTE b); + +/* send string */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : str - string to be sent + length - max length of given string + (limited to USART_SEND_MAX_LENGTH) + * ----------------------------------------------------------------------- */ +void usartSendString(BYTE *str, const UINT16 length); + +/* send string from program memory */ +/* ----------------------------------------------------------------------- + RETURN VAL : NONE + ARGS : str - string to be sent + * ----------------------------------------------------------------------- */ +void usartSendString_P(const BYTE *str); + + +#endif /* _USART_H */ +/* END */ diff --git a/tools/avr-gcc b/tools/avr-gcc new file mode 100644 index 0000000..e69de29 diff --git a/tools/colorgcc b/tools/colorgcc new file mode 100644 index 0000000..1108c44 --- /dev/null +++ b/tools/colorgcc @@ -0,0 +1,322 @@ +#! /usr/bin/perl -w + +# +# colorgcc +# +# Version: 1.3.2 +# +# $Id: colorgcc,v 1.10 1999/04/29 17:15:52 jamoyers Exp $ +# +# A wrapper to colorize the output from compilers whose messages +# match the "gcc" format. +# +# Requires the ANSIColor module from CPAN. +# +# Usage: +# +# In a directory that occurs in your PATH _before_ the directory +# where the compiler lives, create a softlink to colorgcc for +# each compiler you want to colorize: +# +# g++ -> colorgcc +# gcc -> colorgcc +# cc -> colorgcc +# etc. +# +# That's it. When "g++" is invoked, colorgcc is run instead. +# colorgcc looks at the program name to figure out which compiler to run. +# +# The default settings can be overridden with ~/.colorgccrc. +# See the comments in the sample .colorgccrc for more information. +# +# Note: +# +# colorgcc will only emit color codes if: +# +# (1) Its STDOUT is a tty and +# (2) the value of $TERM is not listed in the "nocolor" option. +# +# If colorgcc colorizes the output, the compiler's STDERR will be +# combined with STDOUT. Otherwise, colorgcc just passes the output from +# the compiler through without modification. +# +# Author: Jamie Moyers +# Started: April 20, 1999 +# Licence: GNU Public License +# +# Credits: +# +# I got the idea for this from a script called "color_cvs": +# color_cvs .03 Adrian Likins +# +# (Scott Harrington) +# Much improved handling of compiler command line arguments. +# exec compiler when not colorizing to preserve STDOUT, STDERR. +# Fixed my STDIN kludge. +# +# (Elias S. G. Carotti) +# Corrected handling of text like -DPACKAGE=\"Package\" +# Spotted return code bug. +# +# (Erwin S. Andreasen) +# (Steve Churchill) +# Return code bug fixes. +# +# (Rik Hemsley) +# Found STDIN bug. +# +# Changes: +# +# 1.3.2 Better handling of command line arguments to compiler. +# +# If we aren't colorizing output, we just exec the compiler which +# preserves the original STDOUT and STDERR. +# +# Removed STDIN kludge. STDIN being passed correctly now. +# +# 1.3.1 Added kludge to copy STDIN to the compiler's STDIN. +# +# 1.3.0 Now correctly returns (I hope) the return code of the compiler +# process as its own. +# +# 1.2.1 Applied patch to handle text similar to -DPACKAGE=\"Package\". +# +# 1.2.0 Added tty check. If STDOUT is not a tty, don't do color. +# +# 1.1.0 Added the "nocolor" option to turn off the color if the terminal type +# ($TERM) is listed. +# +# 1.0.0 Initial Version + +use strict; + +use Term::ANSIColor; +use IPC::Open3; +use Cwd 'abs_path'; + +my(%nocolor, %colors, %compilerPaths); +my($unfinishedQuote, $previousColor); + +sub initDefaults +{ + $nocolor{"dumb"} = "true"; + + $colors{"srcColor"} = color("cyan"); + $colors{"introColor"} = color("blue"); + + $colors{"warningFileNameColor"} = color("yellow"); + $colors{"warningNumberColor"} = color("yellow"); + $colors{"warningMessageColor"} = color("yellow"); + + $colors{"errorFileNameColor"} = color("bold red"); + $colors{"errorNumberColor"} = color("bold red"); + $colors{"errorMessageColor"} = color("bold red"); +} + +sub loadPreferences +{ +# Usage: loadPreferences("filename"); + + my($filename) = @_; + + open(PREFS, "<$filename") || return; + + while() + { + next if (m/^\#.*/); # It's a comment. + next if (!m/(.*):\s*(.*)/); # It's not of the form "foo: bar". + + my $option = $1; + my $value = $2; + + if ($option eq "nocolor") + { + # The nocolor option lists terminal types, separated by + # spaces, not to do color on. + foreach my $term (split(' ', $value)) + { + $nocolor{$term} = 1; + } + } + elsif (defined $colors{$option}) + { + $colors{$option} = color($value); + } + else + { + $compilerPaths{$option} = $value; + } + } + close(PREFS); +} + +sub srcscan +{ +# Usage: srcscan($text, $normalColor) +# $text -- the text to colorize +# $normalColor -- The escape sequence to use for non-source text. + +# Looks for text between ` and ', and colors it srcColor. + + my($line, $normalColor) = @_; + + if (defined $normalColor) + { + $previousColor = $normalColor; + } + else + { + $normalColor = $previousColor; + } + + my($srcon) = color("reset") . $colors{"srcColor"}; + my($srcoff) = color("reset") . $normalColor; + + $line = ($unfinishedQuote? $srcon : $normalColor) . $line; + + # These substitutions replaces `foo' with `AfooB' where A is the escape + # sequence that turns on the the desired source color, and B is the + # escape sequence that returns to $normalColor. + + # Handle multi-line quotes. + if ($unfinishedQuote) { + if ($line =~ s/^([^\`]*?)\'/$1$srcoff\'/) + { + $unfinishedQuote = 0; + } + } + if ($line =~ s/\`([^\']*?)$/\`$srcon$1/) + { + $unfinishedQuote = 1; + } + + # Single line quoting. + $line =~ s/\`(.*?)\'/\`$srcon$1$srcoff\'/g; + + print($line, color("reset")); +} + +# +# Main program +# + +# Set up default values for colors and compilers. +initDefaults(); + +# Read the configuration file, if there is one. +my $configFile = $ENV{"HOME"} . "/.colorgccrc"; +if (-f $configFile) +{ + loadPreferences($configFile); +} +elsif (-f '/etc/colorgcc/colorgccrc') +{ + loadPreferences('/etc/colorgcc/colorgccrc'); +} + +# Set our default output color. This presumes that any unrecognized output +# is an error. +$previousColor = $colors{"errorMessageColor"}; + +# Figure out which compiler to invoke based on our program name. +$0 =~ m%.*/(.*)$%; +my $progName = $1 || $0; +my $compiler_pid; + +# If called as "colorgcc", just filter STDIN to STDOUT. +if ($progName eq 'colorgcc') +{ + open(GCCOUT, "<&STDIN"); +} +else +{ + # See if the user asked for a specific compiler. + my $compiler; + if (!defined($compiler = $compilerPaths{$progName})) + { + # Find our wrapper dir on the PATH and tweak the PATH to remove + # everything up-to and including our wrapper dir. + if ($0 =~ m#(.*)/#) + { + # We were called with an explicit path, so trim that off the PATH. + my $find = $1; + $find = abs_path($1) unless $find =~ m#^/#; + $ENV{'PATH'} =~ s#.*(^|:)\Q$find\E(:|$)##; + } + else + { + my(@dirs) = split(/:/, $ENV{'PATH'}); + while (defined($_ = shift @dirs)) + { + if (-x "$_/$progName") + { + $ENV{'PATH'} = join(':', @dirs); + last; + } + } + } + $compiler = $progName; + } + + # Get the terminal type. + my $terminal = $ENV{"TERM"} || "dumb"; + + # If it's in the list of terminal types not to color, or if + # we're writing to something that's not a tty, don't do color. + if (! -t STDOUT || $nocolor{$terminal}) + { + exec $compiler, @ARGV + or die("Couldn't exec"); + } + + # Keep the pid of the compiler process so we can get its return + # code and use that as our return code. + $compiler_pid = open3('<&STDIN', \*GCCOUT, \*GCCOUT, $compiler, @ARGV); +} + +# Colorize the output from the compiler. +while() +{ + if (m#^(.+?\.[^:/ ]+):([0-9]+):(.*)$#) # filename:lineno:message + { + my $field1 = $1 || ""; + my $field2 = $2 || ""; + my $field3 = $3 || ""; + + if ($field3 =~ m/\s+warning:.*/) + { + # Warning + print($colors{"warningFileNameColor"}, "$field1:", color("reset")); + print($colors{"warningNumberColor"}, "$field2:", color("reset")); + srcscan($field3, $colors{"warningMessageColor"}); + } + else + { + # Error + print($colors{"errorFileNameColor"}, "$field1:", color("reset")); + print($colors{"errorNumberColor"}, "$field2:", color("reset")); + srcscan($field3, $colors{"errorMessageColor"}); + } + print("\n"); + } + elsif (m/^:.+`.*'$/) # filename:message: + { + srcscan($_, $colors{"warningMessageColor"}); + } + elsif (m/^(.*?):(.+):$/) # filename:message: + { + # No line number, treat as an "introductory" line of text. + srcscan($_, $colors{"introColor"}); + } + else # Anything else. + { + srcscan($_, undef); + } +} + +if ($compiler_pid) +{ + # Get the return code of the compiler and exit with that. + waitpid($compiler_pid, 0); + exit ($? >> 8); +}