diff --git a/sys/avr/README b/sys/avr/README new file mode 100644 index 00000000..511c40b2 --- /dev/null +++ b/sys/avr/README @@ -0,0 +1,14 @@ +This directory contains code that can be shared across all AVR uC. + +Please refer to https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform to understand what is here. + +At lib/u8x8_avr.h, you can find: + + u8x8_byte_avr_hw_spi + Implements HW SPI communication. To use it, you're required to define SCK and MOSI ports externally (see example Makefiles). + u8x8_avr_delay + Implements the delay functions that are required by the GPIO and Delay callback function. + +This means, you have to write little code to support your own project. + +Please refer to the existing main.c and Makefile examples for details on how to use it. \ No newline at end of file diff --git a/sys/avr/at90usb1286/hello/makefile b/sys/avr/at90usb1286/Makefile similarity index 67% rename from sys/avr/at90usb1286/hello/makefile rename to sys/avr/at90usb1286/Makefile index cc352443..5cc90506 100644 --- a/sys/avr/at90usb1286/hello/makefile +++ b/sys/avr/at90usb1286/Makefile @@ -4,7 +4,6 @@ SIZE = avr-size MCU = at90usb1286 F_CPU = 16000000 U8G2_SRC = ../../../csrc -AVR_LIB_SRC = ../lib CFLAGS = \ -mmcu=$(MCU) \ -DF_CPU=$(F_CPU)UL \ @@ -14,12 +13,17 @@ CFLAGS = \ -ffunction-sections \ -fdata-sections \ -I$(U8G2_SRC)/ \ - -I$(AVR_LIB_SRC) + -I../lib/ \ + -DSCK_DDR=DDRB \ + -DSCK_BIT=1 \ + -DMOSI_DDR=DDRB \ + -DMOSI_BIT=2 LDFLAGS = \ -Wl,--gc-sections \ -mmcu=$(MCU) +TEENSY_LOADER = teensy_loader_cli -SRC = $(shell ls $(U8G2_SRC)/*.c) $(shell ls $(AVR_LIB_SRC)/*.c) main.c +SRC = $(shell ls $(U8G2_SRC)/*.c) $(shell ls ../lib/*.c) $(shell ls ./*.c) OBJ = $(SRC:.c=.o) @@ -34,3 +38,7 @@ size: main.elf clean: rm -f $(OBJ) main.elf main.hex + +# Example for Teensy 2.0++ +upload: main.hex + $(TEENSY_LOADER) -mmcu=$(MCU) -w -v main.hex \ No newline at end of file diff --git a/sys/avr/at90usb1286/hello/main.c b/sys/avr/at90usb1286/hello/main.c deleted file mode 100644 index 66324ab3..00000000 --- a/sys/avr/at90usb1286/hello/main.c +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include - -#define P_CPU_NS (1000000000UL / F_CPU) - -u8g2_t u8g2; - -uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) -{ - // Re-use library for delays - if(u8x8_avr_delay(u8x8, msg, arg_int, arg_ptr)) - return 1; - - switch(msg){ - case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8 - DDRB |= (1<<0); // CS - DDRB |= (1<<4); // DC - DDRA |= (1<<7); // RESET - break; // can be used to setup pins - case U8X8_MSG_GPIO_CS: // CS (chip select) pin: Output level in arg_int - if(arg_int) - PORTB |= (1<<0); - else - PORTB &= ~(1<<0); - break; - case U8X8_MSG_GPIO_DC: // DC (data/cmd, A0, register select) pin: Output level in arg_int - if(arg_int) - PORTB |= (1<<4); - else - PORTB &= ~(1<<4); - break; - case U8X8_MSG_GPIO_RESET: // Reset pin: Output level in arg_int - if(arg_int) - PORTA |= (1<<7); - else - PORTA &= ~(1<<7); - break; - default: - u8x8_SetGPIOResult(u8x8, 1); // default return value - break; - } - return 1; -} - -int main(void) -{ - u8g2_Setup_sh1106_128x64_vcomh0_f( - &u8g2, U8G2_R0, - u8x8_byte_avr_hw_spi, - u8x8_gpio_and_delay - ); - u8g2_InitDisplay(&u8g2); - u8g2_SetPowerSave(&u8g2, 0); - while(1){ - u8g2_ClearBuffer(&u8g2); - u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); - u8g2_DrawStr(&u8g2, 0, 15, "Hello!"); - u8g2_SendBuffer(&u8g2); - } -} \ No newline at end of file diff --git a/sys/avr/at90usb1286/lib/u8x8_avr.c b/sys/avr/at90usb1286/lib/u8x8_avr.c deleted file mode 100644 index fb2e4b96..00000000 --- a/sys/avr/at90usb1286/lib/u8x8_avr.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "u8x8_avr.h" -#include - -#define P_CPU_NS (1000000000UL / F_CPU) - -uint8_t u8x8_byte_avr_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { - uint8_t *data; - - switch(msg) { - case U8X8_MSG_BYTE_INIT: - DDRB |= (1 << 1); // CLOCK (SCLK) - DDRB |= (1 << 2); // DATA (MOSI) - SPSR |= (1 << SPI2X); // SPI2X: Double SPI Speed bit - SPCR = ((1 << SPE) | (1 << MSTR)); // Enable / Master - u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level); - break; - case U8X8_MSG_BYTE_SET_DC: - u8x8_gpio_SetDC(u8x8, arg_int); - break; - case U8X8_MSG_BYTE_START_TRANSFER: - u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level); - u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL); - break; - case U8X8_MSG_BYTE_SEND: - data = (uint8_t *)arg_ptr; - while( arg_int > 0 ) { - SPDR = (uint8_t)*data; - while (!(SPSR & _BV(SPIF))); - data++; - arg_int--; - } - break; - case U8X8_MSG_BYTE_END_TRANSFER: - u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL); - u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level); - break; - default: - return 0; - } - return 1; -} - -uint8_t u8x8_avr_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) -{ - uint8_t cycles; - - switch(msg){ - case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second - // At 20Mhz, each cycle is 50ns, the call itself is slower. - break; - case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds - // Approximate best case values... - #define CALL_CYCLES 26UL - #define CALC_CYCLES 4UL - #define RETURN_CYCLES 4UL - #define CYCLES_PER_LOOP 4UL - - cycles = (100UL * arg_int) / (P_CPU_NS * CYCLES_PER_LOOP); - - if(cycles > CALL_CYCLES + RETURN_CYCLES + CALC_CYCLES) break; - - __asm__ __volatile__ ( - "1: sbiw %0,1" "\n\t" // 2 cycles - "brne 1b" : "=w" (cycles) : "0" (cycles) // 2 cycles - ); - break; - case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds - for(int i=0 ; i < arg_int ; i++) - _delay_us(10); - break; - case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second - for(int i=0 ; i < arg_int ; i++) - _delay_ms(1); - break; - default: - return 0; - } - return 1; -} diff --git a/sys/avr/at90usb1286/lib/u8x8_avr.h b/sys/avr/at90usb1286/lib/u8x8_avr.h deleted file mode 100644 index 69497d85..00000000 --- a/sys/avr/at90usb1286/lib/u8x8_avr.h +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include - -uint8_t u8x8_byte_avr_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); -uint8_t u8x8_avr_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); \ No newline at end of file diff --git a/sys/avr/at90usb1286/main.c b/sys/avr/at90usb1286/main.c new file mode 100644 index 00000000..e1309df3 --- /dev/null +++ b/sys/avr/at90usb1286/main.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +#define CS_DDR DDRB +#define CS_PORT PORTB +#define CS_BIT 0 + +#define DC_DDR DDRB +#define DC_PORT PORTB +#define DC_BIT 4 + +#define RESET_DDR DDRA +#define RESET_PORT PORTA +#define RESET_BIT 7 + + +u8g2_t u8g2; + +uint8_t +u8x8_gpio_and_delay (u8x8_t * u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + // Re-use library for delays + if (u8x8_avr_delay(u8x8, msg, arg_int, arg_ptr)) + return 1; + + switch (msg) { + // called once during init phase of u8g2/u8x8 + // can be used to setup pins + case U8X8_MSG_GPIO_AND_DELAY_INIT: + CS_DDR |= _BV(CS_BIT); + DC_DDR |= _BV(DC_BIT); + RESET_DDR |= _BV(RESET_BIT); + break; + // CS (chip select) pin: Output level in arg_int + case U8X8_MSG_GPIO_CS: + if (arg_int) + CS_PORT |= _BV(CS_BIT); + else + CS_PORT &= ~_BV(CS_BIT); + break; + // DC (data/cmd, A0, register select) pin: Output level in arg_int + case U8X8_MSG_GPIO_DC: + if (arg_int) + DC_PORT |= _BV(DC_BIT); + else + DC_PORT &= ~_BV(DC_BIT); + break; + // Reset pin: Output level in arg_int + case U8X8_MSG_GPIO_RESET: + if (arg_int) + RESET_PORT |= _BV(RESET_BIT); + else + RESET_PORT &= ~_BV(RESET_BIT); + break; + default: + u8x8_SetGPIOResult(u8x8, 1); + break; + } + return 1; +} + +int main (void) { + u8g2_Setup_sh1106_128x64_vcomh0_f( + &u8g2, U8G2_R0, + u8x8_byte_avr_hw_spi, + u8x8_gpio_and_delay + ); + u8g2_InitDisplay(&u8g2); + u8g2_SetPowerSave(&u8g2, 0); + + while (1) { + u8g2_ClearBuffer(&u8g2); + u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); + u8g2_DrawStr(&u8g2, 0, 15, "Hello!"); + u8g2_SendBuffer(&u8g2); + } +} diff --git a/sys/avr/atmega328/main.c b/sys/avr/atmega328/main.c deleted file mode 100644 index be7d0312..00000000 --- a/sys/avr/atmega328/main.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - - main.c - - AVR Test Project - This project will use 4-Wire SW SPI - - Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/) - - Copyright (c) 2018, olikraus@gmail.com - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list - of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - - -#include -#include -#include - -#define DISPLAY_CLK_DIR DDRB -#define DISPLAY_CLK_PORT PORTB -#define DISPLAY_CLK_PIN 5 - -#define DISPLAY_DATA_DIR DDRB -#define DISPLAY_DATA_PORT PORTB -#define DISPLAY_DATA_PIN 3 - -#define DISPLAY_CS_DIR DDRB -#define DISPLAY_CS_PORT PORTB -#define DISPLAY_CS_PIN 2 - -#define DISPLAY_DC_DIR DDRB -#define DISPLAY_DC_PORT PORTB -#define DISPLAY_DC_PIN 1 - -#define DISPLAY_RESET_DIR DDRB -#define DISPLAY_RESET_PORT PORTB -#define DISPLAY_RESET_PIN 0 - - - - -#define P_CPU_NS (1000000000UL / F_CPU) - -u8g2_t u8g2; - -uint8_t u8x8_avr_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) -{ - uint8_t cycles; - - switch(msg) - { - case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second - // At 20Mhz, each cycle is 50ns, the call itself is slower. - break; - case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds - // Approximate best case values... -#define CALL_CYCLES 26UL -#define CALC_CYCLES 4UL -#define RETURN_CYCLES 4UL -#define CYCLES_PER_LOOP 4UL - - cycles = (100UL * arg_int) / (P_CPU_NS * CYCLES_PER_LOOP); - - if(cycles > CALL_CYCLES + RETURN_CYCLES + CALC_CYCLES) - break; - - __asm__ __volatile__ ( - "1: sbiw %0,1" "\n\t" // 2 cycles - "brne 1b" : "=w" (cycles) : "0" (cycles) // 2 cycles - ); - break; - case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds - for(int i=0 ; i < arg_int ; i++) - _delay_us(10); - break; - case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second - for(int i=0 ; i < arg_int ; i++) - _delay_ms(1); - break; - default: - return 0; - } - return 1; -} - - -uint8_t u8x8_avr_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) -{ - // Re-use library for delays - - switch(msg) - { - case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8 - DISPLAY_CLK_DIR |= 1< +#include +#include +#include + +#define CS_DDR DDRB +#define CS_PORT PORTB +#define CS_BIT 2 + +#define DC_DDR DDRB +#define DC_PORT PORTB +#define DC_BIT 1 + +#define RESET_DDR DDRB +#define RESET_PORT PORTB +#define RESET_BIT 0 + + +u8g2_t u8g2; + +uint8_t +u8x8_gpio_and_delay (u8x8_t * u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + // Re-use library for delays + if (u8x8_avr_delay(u8x8, msg, arg_int, arg_ptr)) + return 1; + + switch (msg) { + // called once during init phase of u8g2/u8x8 + // can be used to setup pins + case U8X8_MSG_GPIO_AND_DELAY_INIT: + CS_DDR |= _BV(CS_BIT); + DC_DDR |= _BV(DC_BIT); + RESET_DDR |= _BV(RESET_BIT); + break; + // CS (chip select) pin: Output level in arg_int + case U8X8_MSG_GPIO_CS: + if (arg_int) + CS_PORT |= _BV(CS_BIT); + else + CS_PORT &= ~_BV(CS_BIT); + break; + // DC (data/cmd, A0, register select) pin: Output level in arg_int + case U8X8_MSG_GPIO_DC: + if (arg_int) + DC_PORT |= _BV(DC_BIT); + else + DC_PORT &= ~_BV(DC_BIT); + break; + // Reset pin: Output level in arg_int + case U8X8_MSG_GPIO_RESET: + if (arg_int) + RESET_PORT |= _BV(RESET_BIT); + else + RESET_PORT &= ~_BV(RESET_BIT); + break; + default: + u8x8_SetGPIOResult(u8x8, 1); + break; + } + return 1; +} + +int main (void) { + u8g2_Setup_sh1106_128x64_noname_f( + &u8g2, U8G2_R0, + u8x8_byte_avr_hw_spi, + u8x8_gpio_and_delay + ); + u8g2_InitDisplay(&u8g2); + u8g2_SetPowerSave(&u8g2, 0); + + while (1) { + u8g2_ClearBuffer(&u8g2); + u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr); + u8g2_DrawStr(&u8g2, 0, 15, "Hello!"); + u8g2_SendBuffer(&u8g2); + } +} diff --git a/sys/avr/lib/u8x8_avr.c b/sys/avr/lib/u8x8_avr.c new file mode 100644 index 00000000..1564e034 --- /dev/null +++ b/sys/avr/lib/u8x8_avr.c @@ -0,0 +1,139 @@ +#include "u8x8_avr.h" +#include + +#define P_CPU_NS (1000000000UL / F_CPU) + +#ifndef SCK_DDR +#error "SCK_DDR must be defined externally, eg: -DSCK_DDR=DDRB, for atmega328p." +#endif + +#ifndef SCK_BIT +#error "SCK_BIT must be defined externally, eg: -DSCK_BIT=5, for atmega328p." +#endif + +#ifndef MOSI_DDR +#error "MOSI_DDR must be defined externally, eg: -DMOSI_DDR=DDRB, for atmega328p." +#endif + +#ifndef MOSI_BIT +#error "MOSI_BIT must be defined externally, eg: -DMOSI_BIT=3, for atmega328p." +#endif + +uint8_t u8x8_byte_avr_hw_spi (u8x8_t * u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + uint8_t *data; + + switch (msg) { + case U8X8_MSG_BYTE_INIT: + SCK_DDR |= _BV (SCK_BIT); + MOSI_DDR |= _BV (MOSI_BIT); + + SPCR = (_BV (SPE) | _BV (MSTR)); + + switch (u8x8->display_info->spi_mode) { + case 0: + break; + case 1: + SPCR |= _BV (CPHA); + break; + case 2: + SPCR |= _BV (CPOL); + break; + case 3: + SPCR |= _BV (CPOL); + SPCR |= _BV (CPHA); + break; + }; + + switch (F_CPU / u8x8->display_info->sck_clock_hz) { + case 2: + SPSR |= _BV (SPI2X); + break; + case 4: + break; + case 8: + SPSR |= _BV (SPI2X); + SPCR |= _BV (SPR0); + break; + case 16: + SPCR |= _BV (SPR0); + break; + case 32: + SPSR |= _BV (SPI2X); + SPCR |= _BV (SPR1); + break; + case 64: + SPCR |= _BV (SPR1); + break; + case 128: + SPCR |= _BV (SPR1); + SPCR |= _BV (SPR0); + break; + } + + u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level); + break; + case U8X8_MSG_BYTE_SET_DC: + u8x8_gpio_SetDC(u8x8, arg_int); + break; + case U8X8_MSG_BYTE_START_TRANSFER: + u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level); + u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL); + break; + case U8X8_MSG_BYTE_SEND: + data = (uint8_t *) arg_ptr; + while (arg_int > 0) { + SPDR = (uint8_t) * data; + while (!(SPSR & _BV (SPIF))); + data++; + arg_int--; + } + break; + case U8X8_MSG_BYTE_END_TRANSFER: + u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL); + u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level); + break; + default: + return 0; + } + + return 1; +} + +uint8_t u8x8_avr_delay (u8x8_t * u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + uint8_t cycles; + + switch (msg) { + case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second + // At 20Mhz, each cycle is 50ns, the call itself is slower. + break; + case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds + // Approximate best case values... + #define CALL_CYCLES 26UL + #define CALC_CYCLES 4UL + #define RETURN_CYCLES 4UL + #define CYCLES_PER_LOOP 4UL + + cycles = (100UL * arg_int) / (P_CPU_NS * CYCLES_PER_LOOP); + + if (cycles > CALL_CYCLES + RETURN_CYCLES + CALC_CYCLES) + break; + + __asm__ __volatile__ ( + "1: sbiw %0,1" "\n\t" // 2 cycles + "brne 1b":"=w" (cycles):"0" (cycles) // 2 cycles + ); + break; + case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds + for (int i = 0; i < arg_int; i++) + _delay_us(10); + break; + case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second + for (int i = 0; i < arg_int; i++) + _delay_ms(1); + break; + default: + return 0; + } + + return 1; +} diff --git a/sys/avr/lib/u8x8_avr.h b/sys/avr/lib/u8x8_avr.h new file mode 100644 index 00000000..11dacb31 --- /dev/null +++ b/sys/avr/lib/u8x8_avr.h @@ -0,0 +1,5 @@ +#include +#include + +uint8_t u8x8_byte_avr_hw_spi(u8x8_t * u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); +uint8_t u8x8_avr_delay(u8x8_t * u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); \ No newline at end of file