summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@diac24.net>2011-08-21 14:56:43 +0200
committerDavid Lamparter <equinox@diac24.net>2011-08-21 14:56:43 +0200
commitaa0a617f957de3c42a217283b8cc35db669d270c (patch)
treea04f6e7ed604abfc0dad81d1230158aba9070528
initial commit
-rw-r--r--.gitignore6
-rw-r--r--Makefile21
-rw-r--r--avr4-signature.x212
-rw-r--r--can.c222
-rw-r--r--cantiming.py78
-rw-r--r--uart.c78
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) }
+}
diff --git a/can.c b/can.c
new file mode 100644
index 0000000..6120d51
--- /dev/null
+++ b/can.c
@@ -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))
diff --git a/uart.c b/uart.c
new file mode 100644
index 0000000..df3243f
--- /dev/null
+++ b/uart.c
@@ -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);
+}
+