diff options
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | avr4-signature.x | 212 | ||||
-rw-r--r-- | can.c | 222 | ||||
-rw-r--r-- | cantiming.py | 78 | ||||
-rw-r--r-- | uart.c | 78 |
6 files changed, 617 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acfe7e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.elf +*.flash +*.hex +*.o +*.pyc +*.pyo diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..23fc0af --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.PHONY: flash love + +TARGET=can +CFLAGS_OPT=-Os \ + -fpredictive-commoning -fmerge-all-constants -fmodulo-sched -fmodulo-sched-allow-regmoves \ + -fgcse-sm -fgcse-las -fgcse-after-reload -fconserve-stack \ + -fwhole-program +CFLAGS_WARN=-std=gnu99 -Wall -Wextra -Wno-unused -pedantic +CFLAGS_LD=-Wl,-T,avr4-signature.x +CFLAGS=-mmcu=atmega48 ${CFLAGS_WARN} ${CFLAGS_OPT} ${CFLAGS_LD} + +love: ${TARGET}.elf + +flash: ${TARGET}.flash + avrdude -p m48 -c stk500v2 -P avrdoper -y -U flash:w:$< + +%.flash: %.elf Makefile + avr-objcopy -j .text -j .data -O ihex $< $@ +%.elf: %.c *.c *.x Makefile + avr-gcc ${CFLAGS} -o $@ $< + avr-size $@ diff --git a/avr4-signature.x b/avr4-signature.x new file mode 100644 index 0000000..28bec55 --- /dev/null +++ b/avr4-signature.x @@ -0,0 +1,212 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") +OUTPUT_ARCH(avr:4) +MEMORY +{ + text (rx) : ORIGIN = 0, LENGTH = 8K + data (rw!x) : ORIGIN = 0x800060, LENGTH = 0xffa0 + eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K +} +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : + { + *(.rel.text) + *(.rel.text.*) + *(.rel.gnu.linkonce.t*) + } + .rela.text : + { + *(.rela.text) + *(.rela.text.*) + *(.rela.gnu.linkonce.t*) + } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : + { + *(.rel.rodata) + *(.rel.rodata.*) + *(.rel.gnu.linkonce.r*) + } + .rela.rodata : + { + *(.rela.rodata) + *(.rela.rodata.*) + *(.rela.gnu.linkonce.r*) + } + .rel.data : + { + *(.rel.data) + *(.rel.data.*) + *(.rel.gnu.linkonce.d*) + } + .rela.data : + { + *(.rela.data) + *(.rela.data.*) + *(.rela.gnu.linkonce.d*) + } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + /* Internal text space or external memory. */ + .text : + { + *(.vectors) + KEEP(*(.vectors)) + /* For data that needs to reside in the lower 64k of progmem. */ + *(.progmem.gcc*) + *(.progmem*) + . = ALIGN(2); + __trampolines_start = . ; + /* The jump trampolines for the 16-bit limited relocs will reside here. */ + *(.trampolines) + *(.trampolines*) + __trampolines_end = . ; + /* For future tablejump instruction arrays for 3 byte pc devices. + We don't relax jump/call instructions within these sections. */ + *(.jumptables) + *(.jumptables*) + /* For code that needs to reside in the lower 128k progmem. */ + *(.lowtext) + *(.lowtext*) + __ctors_start = . ; + *(.ctors) + __ctors_end = . ; + __dtors_start = . ; + *(.dtors) + __dtors_end = . ; + KEEP(SORT(*)(.ctors)) + KEEP(SORT(*)(.dtors)) + /* From this point on, we don't bother about wether the insns are + below or above the 16 bits boundary. */ + *(.init0) /* Start here after reset. */ + KEEP (*(.init0)) + *(.init1) + KEEP (*(.init1)) + *(.init2) /* Clear __zero_reg__, set up stack pointer. */ + KEEP (*(.init2)) + *(.init3) + KEEP (*(.init3)) + *(.init4) /* Initialize data and BSS. */ + KEEP (*(.init4)) + *(.init5) + KEEP (*(.init5)) + *(.init6) /* C++ constructors. */ + KEEP (*(.init6)) + *(.init7) + KEEP (*(.init7)) + *(.init8) + KEEP (*(.init8)) + *(.init9) /* Call main(). */ + KEEP (*(.init9)) + *(.text) + . = ALIGN(2); + *(.text.*) + . = ALIGN(2); + *(.fini9) /* _exit() starts here. */ + KEEP (*(.fini9)) + *(.fini8) + KEEP (*(.fini8)) + *(.fini7) + KEEP (*(.fini7)) + *(.fini6) /* C++ destructors. */ + KEEP (*(.fini6)) + *(.fini5) + KEEP (*(.fini5)) + *(.fini4) + KEEP (*(.fini4)) + *(.fini3) + KEEP (*(.fini3)) + *(.fini2) + KEEP (*(.fini2)) + *(.fini1) + KEEP (*(.fini1)) + *(.fini0) /* Infinite loop after program termination. */ + KEEP (*(.fini0)) + _etext = . ; + } > text + .data : AT (ADDR (.text) + SIZEOF (.text)) + { + PROVIDE (__data_start = .) ; + *(.data) + *(.data*) + *(.rodata) /* We need to include .rodata here if gcc is used */ + *(.rodata*) /* with -fdata-sections. */ + *(.gnu.linkonce.d*) + . = ALIGN(2); + _edata = . ; + PROVIDE (__data_end = .) ; + } > data + .bss : AT (ADDR (.bss)) + { + PROVIDE (__bss_start = .) ; + *(.bss) + *(.bss*) + *(COMMON) + PROVIDE (__bss_end = .) ; + } > data + __data_load_start = LOADADDR(.data); + __data_load_end = __data_load_start + SIZEOF(.data); + /* Global data not cleared after reset. */ + .noinit : + { + PROVIDE (__noinit_start = .) ; + *(.noinit*) + PROVIDE (__noinit_end = .) ; + _end = . ; + PROVIDE (__heap_start = .) ; + } > data + .eeprom : + { + *(.eeprom*) + __eeprom_end = . ; + } > eeprom + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) *(.gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* AVR signature */ + .signature 0 (INFO) : { *(.signature) } +} @@ -0,0 +1,222 @@ +#define F_CPU 8000000 +#include <stdint.h> +#include <stdbool.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/pgmspace.h> +#include <util/delay.h> + +const uint8_t __signature[3] __attribute__((section (".signature"), used)) = + { SIGNATURE_2, SIGNATURE_1, SIGNATURE_0 }; + +#include "uart.c" + +#define B_SCK 5 +#define B_MISO 4 +#define B_MOSI 3 +#define B_SS 2 + +#define spi_ss(x) PORTB = ((x) << B_SS) | 0x3; + +static volatile bool canint = false; + +ISR(INT0_vect) +{ + uart_puts("can: interrupt\n"); + canint = true; +} + +static uint8_t spi_wrrd(uint8_t out) +{ + SPDR = out; + while (!(SPSR & (1 << SPIF))) + ; + return SPDR; +} + +static void spi_performpgm(const prog_uint8_t *cmds, uint8_t len) +{ + const prog_uint8_t *end = cmds + len; + uint8_t c; + + spi_ss(0); + while (cmds < end) { + c = pgm_read_byte(cmds); + spi_wrrd(c); + cmds++; + } + spi_ss(1); +} +#define spi_perform(...) do { \ + static const uint8_t _mycmds[] PROGMEM = { __VA_ARGS__ }; \ + spi_performpgm(_mycmds, sizeof(_mycmds)); } while (0) + +/* CAN configuration: + * + * CNF1 SJW = 1TQ 00xxxxxx + * BRP = /12 xx001011 (16 MHz assumed) + * = 0x0b + * CNF2 BTLMODE = cfg 1xxxxxxx + * SAM = once x0xxxxxx + * PS1 = 1TQ xx000xxx + * prop = 2TQ xxxxx001 + * = 0x81 + * CNF3 WAKFIL = off x0xxxxxx + * PS2 = 2TQ xxxxx001 + * = 0x01 + */ + +#define MCP2515_WRITE 0x02 +#define MCP2515_READ 0x03 +#define MCP2515_RTS 0x80 + +#define A_CNF3 0x28 +#define A_CANINTF 0x2c +#define A_CANCTRL 0x2f +#define A_TXB0CTRL 0x30 +#define A_TXB0SIDH 0x31 +#define A_RXB1CTRL 0x70 + +static void can_init(void) +{ + spi_perform(MCP2515_WRITE, A_CANCTRL, + 0x80); /* CANCTRL: config mode */ + spi_perform(MCP2515_WRITE, A_CNF3, + 0x01, /* CNF3 */ + 0x81, /* CNF2 */ + 0x0b, /* CNF1 */ + 0xa4 /* CANINTE: MERRE, ERRIE, TX0IE */ + ); + spi_perform(MCP2515_WRITE, A_RXB1CTRL, + 0x60); /* x, RXM, x, RXRTR, FILHIT */ + spi_perform(MCP2515_WRITE, A_CANCTRL, + 0x00); /* CANCTRL: normal mode */ +} + +static uint8_t can_CANSTAT(void) +{ + uint8_t canstat; + + spi_ss(0); + spi_wrrd(0x03); + spi_wrrd(0x2e); /* addr(CANCTRL) */ + canstat = spi_wrrd(0xff); /* CANSTAT */ + spi_ss(1); + uart_puts("can: CANSTAT "); + uart_puthex(canstat); + uart_puts("\n"); + return canstat; +} + +static void can_send(void) +{ + uart_puts("can: transmit\n"); + spi_perform(MCP2515_WRITE, A_TXB0SIDH, + 0x55, /* ID 10:3 */ + 0x40, /* ID 2:0, x, EXIDE, x, EID 17:16 */ + 0x00, /* EID 15:8 */ + 0x00, /* EID 7:0 */ + 0x00); /* x, RTR, xx, DLC */ + spi_perform(MCP2515_RTS | 0x01); +} + +static void can_int(void) +{ + uint8_t canintf, eflg, canstat; + + uart_puts("can: irqh<"); + + spi_ss(0); + spi_wrrd(MCP2515_READ); + spi_wrrd(A_CANINTF); + canintf = spi_wrrd(0xff); + eflg = spi_wrrd(0xff); + canstat = spi_wrrd(0xff); + spi_ss(1); + + uart_puthex(canintf); + uart_puthex(eflg); + uart_puthex(canstat); + uart_puts(">\n"); + + if (canintf & 0x80 || canintf & 0x04) { + uint8_t txb0ctrl; + spi_ss(0); + spi_wrrd(0x03); + spi_wrrd(0x30); + txb0ctrl = spi_wrrd(0xff); + spi_ss(1); + uart_puts("can: TXB0CTRL "); + uart_puthex(txb0ctrl); + uart_puts("\n"); + } + if (canintf & 0x02) { + uint8_t rxb1dlc, c; + uart_puts("can: RX1IF\n"); + spi_ss(0); + spi_wrrd(0x94); + uart_puthex(spi_wrrd(0xff)); + uart_puthex(spi_wrrd(0xff)); + uart_puthex(spi_wrrd(0xff)); + uart_puthex(spi_wrrd(0xff)); + rxb1dlc = spi_wrrd(0xff); + uart_puthex(rxb1dlc); + uart_puts("\n"); + for (c = 0; c < rxb1dlc; c++) + uart_puthex(spi_wrrd(0xff)); + uart_puts("\n"); + spi_ss(1); + } + + spi_perform(MCP2515_WRITE, A_CANINTF, 0x00); +} + +int main(void) +{ + uint8_t status; + + uart_init(); + + spi_ss(1); + DDRB = (1 << B_SCK) | (1 << B_MOSI) | (1 << B_SS); + + /* divisor: 0 0 0 = fosc / 4 = 2 MHz */ + SPCR = (1 << SPE) | (1 << MSTR); + + /* INT0 */ + EICRA = (1 << ISC01); + EIMSK = (1 << INT0); + + sei(); + + uart_puts("\nspi: init ok\n"); + _delay_ms(50); + + spi_ss(0); + spi_wrrd(0xc0); + spi_ss(1); + + _delay_ms(50); + spi_ss(0); + spi_wrrd(0xb0); + status = spi_wrrd(0xff); + spi_ss(1); + uart_puts("can: status "); + uart_puthex(status); + uart_puts("\n"); + + can_init(); + can_CANSTAT(); + + while (1) { + if (canint) { + canint = false; + can_int(); + } + _delay_ms(25); + } +} + +void __do_copy_data(void) __attribute__((naked, section (".init4"), used)); +void __do_copy_data(void) { } + diff --git a/cantiming.py b/cantiming.py new file mode 100644 index 0000000..1eb7fe2 --- /dev/null +++ b/cantiming.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +from math import ceil +def eng(val): + if val < 1e-6: + return '%.3fn' % (val * 1e9) + if val < 1e-3: + return '%.3fµ' % (val * 1e6) + if val < 1e-0: + return '%.3fm' % (val * 1e3) + if val < 1e3: + return '%.3f' % (val) + if val < 1e6: + return '%.3fk' % (val / 1e3) + if val < 1e9: + return '%.3fM' % (val / 1e6) + return '%f' % (val) + +fosc = 16e6 + +brp = 12 +print 'BRP: %d' % (brp) +tdrvcmp = 130e-9 + +tq = 2 * brp / fosc +print 'Tq: %ss (= %sHz)' % (eng(tq), eng(1./tq)) + +print '' + +print 'bus properties:' +print '\tTdrv + Tcmp = %ss' % (eng(tdrvcmp)) +buslen = 100. +tbus = buslen / 177. * 1e-6 +print '\tTbus = %ss' % (eng(tbus)) +tprop = 2 * (tbus + tdrvcmp) +print '\tTprop = %ss, / Tq = %.2f' % (eng(tprop), tprop / tq) +print '' + +s_sync = 1 +s_prop = 2 +s_ps1 = 1 +s_ps2 = 2 +print ' <%s%d%s><%s%d%s><%s%d%s><%s%d%s>' % ( + '-' * (s_sync * 2 - 1), s_sync, + '-' * (s_sync * 2 - 1), + '-' * (s_prop * 2 - 1), s_prop, + '-' * (s_prop * 2 - 1), + '-' * (s_ps1 * 2 - 1), s_ps1, + '-' * (s_ps1 * 2 - 1), + '-' * (s_ps2 * 2 - 1), s_ps2, + '-' * (s_ps2 * 2 - 1)) +print (' %%-%ds%%-%ds%%-%ds%%-%ds' % (s_sync * 5, s_prop * 5, s_ps1 * 5, s_ps2 * 5)) % ('sync', 'prop', 'PS1', 'PS2') +print '' +tqperbit = s_sync + s_prop + s_ps1 + s_ps2 +bittime = tq * tqperbit +print '\tTbit = %ss' % (eng(bittime)) +print '' + +print 'system properties:' +osctol = 0.001 +print '\toscillator tolerance: %dppm' % (osctol / 0.000001) + + +tbitmin, tbitmax = bittime * (1 - osctol), bittime * (1 + osctol) +print '\tTbit \in (%ss, %ss)' % (eng(tbitmin), eng(tbitmax)) +tsjwmin = 10 * tbitmax - 10 * tbitmin +print '\tTsjwmin = %ss, / Tq = %.6f' % (eng(tsjwmin), tsjwmin / (tq * (1 - osctol))) +sjw = int(ceil(tsjwmin / (tq * (1 - osctol)))) +print '\tSJW = %d' % (sjw) +print '' + +print 'conditions' +print '\tprop + PS1 >= PS2 ', str(s_prop + s_ps1 >= s_ps2) +print '\tprop + PS1 >= Tprop ', str(s_prop + s_ps1 >= tprop / tq) +print '\tPS2 > SJW ', str(s_ps2 > sjw) +print '' + +print 'result' +print '\t%sbit/s' % (eng(1. / bittime)) @@ -0,0 +1,78 @@ +static char txbuf[64]; +static uint8_t txput = 0, txsend = 0; + +ISR(USART_RX_vect) +{ + uint8_t data = UDR0; +} + +ISR(USART_UDRE_vect) +{ + if (txsend != txput) { + UDR0 = txbuf[txsend]; + txsend++; + txsend &= 63; + UCSR0B = (1 << RXCIE0) | (1 << UDRIE0) | (1 << RXEN0) | (1 << TXEN0); + } else { + UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0); + } +} + +static void _uart_putch(const char ch) +{ + txbuf[txput] = ch; + txput++; + txput &= 63; +} + +static void uart_putpgm(PGM_P str) +{ + char c; + uint8_t sreg = SREG; + cli(); + + do { + c = pgm_read_byte(str); + if (!c) + break; + _uart_putch(c); + str++; + } while (1); + if (UCSR0A & (1 << UDRE0)) + __vector_19(); + + SREG = sreg; +} +#define uart_puts(str) do { \ + static const char _mystr[] PROGMEM = str; \ + uart_putpgm(_mystr); } while (0) + +#define hexdigit(x) ((x) + '0' + ((x) > 9 ? ('a' - '0' - 10) : 0)) +static void uart_puthex(uint8_t val) +{ + uint8_t tmp, sreg = SREG; + cli(); + + tmp = val >> 4; + tmp += '0'; + if (tmp > '9') + tmp += 'a' - '0' - 10; + _uart_putch(tmp); + tmp = val & 0xf; + tmp += '0'; + if (tmp > '9') + tmp += 'a' - '0' - 10; + _uart_putch(tmp); + if (UCSR0A & (1 << UDRE0)) + __vector_19(); + + SREG = sreg; +} + +static void uart_init(void) +{ + UBRR0 = 12; /* 38461 ~ 38400 */ + UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); + UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0); +} + |