Merge pull request #809 from fornellas/avr

Refactor avr libs.
This commit is contained in:
olikraus 2019-02-09 12:47:39 +01:00 committed by GitHub
commit 03683b469b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 370 additions and 334 deletions

14
sys/avr/README Normal file
View File

@ -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.

View File

@ -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

View File

@ -1,61 +0,0 @@
#include <u8g2.h>
#include <util/delay.h>
#include <u8x8_avr.h>
#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);
}
}

View File

@ -1,79 +0,0 @@
#include "u8x8_avr.h"
#include <util/delay.h>
#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;
}

View File

@ -1,5 +0,0 @@
#include <u8g2.h>
#include <stdint.h>
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);

View File

@ -0,0 +1,78 @@
#include <u8g2.h>
#include <util/delay.h>
#include <u8x8_avr.h>
#include <avr/power.h>
#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);
}
}

View File

@ -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 <avr/io.h>
#include <u8g2.h>
#include <util/delay.h>
#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<<DISPLAY_CLK_PIN;
DISPLAY_DATA_DIR |= 1<<DISPLAY_DATA_PIN;
DISPLAY_CS_DIR |= 1<<DISPLAY_CS_PIN;
DISPLAY_DC_DIR |= 1<<DISPLAY_DC_PIN;
DISPLAY_RESET_DIR |= 1<<DISPLAY_RESET_PIN;
break; // can be used to setup pins
case U8X8_MSG_GPIO_SPI_CLOCK: // Clock pin: Output level in arg_int
if(arg_int)
DISPLAY_CLK_PORT |= (1<<DISPLAY_CLK_PIN);
else
DISPLAY_CLK_PORT &= ~(1<<DISPLAY_CLK_PIN);
break;
case U8X8_MSG_GPIO_SPI_DATA: // MOSI pin: Output level in arg_int
if(arg_int)
DISPLAY_DATA_PORT |= (1<<DISPLAY_DATA_PIN);
else
DISPLAY_DATA_PORT &= ~(1<<DISPLAY_DATA_PIN);
break;
case U8X8_MSG_GPIO_CS: // CS (chip select) pin: Output level in arg_int
if(arg_int)
DISPLAY_CS_PORT |= (1<<DISPLAY_CS_PIN);
else
DISPLAY_CS_PORT &= ~(1<<DISPLAY_CS_PIN);
break;
case U8X8_MSG_GPIO_DC: // DC (data/cmd, A0, register select) pin: Output level in arg_int
if(arg_int)
DISPLAY_DC_PORT |= (1<<DISPLAY_DC_PIN);
else
DISPLAY_DC_PORT &= ~(1<<DISPLAY_DC_PIN);
break;
case U8X8_MSG_GPIO_RESET: // Reset pin: Output level in arg_int
if(arg_int)
DISPLAY_RESET_PORT |= (1<<DISPLAY_RESET_PIN);
else
DISPLAY_RESET_PORT &= ~(1<<DISPLAY_RESET_PIN);
break;
default:
if (u8x8_avr_delay(u8x8, msg, arg_int, arg_ptr)) // check for any delay msgs
return 1;
u8x8_SetGPIOResult(u8x8, 1); // default return value
break;
}
return 1;
}
int main(void)
{
/*
Select a setup procedure for your display from here: https://github.com/olikraus/u8g2/wiki/u8g2setupc
1. Arg: Address of an empty u8g2 structure
2. Arg: Usually U8G2_R0, others are listed here: https://github.com/olikraus/u8g2/wiki/u8g2reference#carduino-example
3. Arg: Protocol procedure (u8g2-byte), list is here: https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform#communication-callback-eg-u8x8_byte_hw_i2c
4. Arg: Defined in this code itself (see above)
*/
u8g2_Setup_st7565_ea_dogm132_f( &u8g2, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_avr_gpio_and_delay );
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);
/* full buffer example, setup procedure ends in _f */
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
u8g2_DrawStr(&u8g2, 1, 18, "U8g2 on AVR");
u8g2_SendBuffer(&u8g2);
while(1){
}
}

View File

@ -0,0 +1,45 @@
CC = avr-gcc
OBJCPY = avr-objcopy
SIZE = avr-size
MCU = atmega328p
F_CPU = 16000000
U8G2_SRC = ../../../csrc
CFLAGS = \
-mmcu=$(MCU) \
-DF_CPU=$(F_CPU)UL \
-Os \
-std=gnu99 \
-Werror \
-ffunction-sections \
-fdata-sections \
-I$(U8G2_SRC)/ \
-I../lib/ \
-DSCK_DDR=DDRB \
-DSCK_BIT=5 \
-DMOSI_DDR=DDRB \
-DMOSI_BIT=3
LDFLAGS = \
-Wl,--gc-sections \
-mmcu=$(MCU)
AVRDUDE=avrdude
PORT=/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A4008fhY-if00-port0
SRC = $(shell ls $(U8G2_SRC)/*.c) $(shell ls ../lib/*.c) $(shell ls ./*.c)
OBJ = $(SRC:.c=.o)
main.hex: main.elf
$(OBJCPY) -O ihex -R .eeprom -R .fuse -R .lock -R .signature main.elf main.hex
main.elf: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $@
size: main.elf
$(SIZE) --mcu=$(MCU) --format=avr main.elf
clean:
rm -f $(OBJ) main.elf main.hex
# Example for Arduino Duemilanove
upload: main.hex
$(AVRDUDE) -p$(MCU) -carduino -P$(PORT) -b57600 -D -Uflash:w:main.hex:i

78
sys/avr/atmega328p/main.c Normal file
View File

@ -0,0 +1,78 @@
#include <u8g2.h>
#include <util/delay.h>
#include <u8x8_avr.h>
#include <avr/power.h>
#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);
}
}

139
sys/avr/lib/u8x8_avr.c Normal file
View File

@ -0,0 +1,139 @@
#include "u8x8_avr.h"
#include <util/delay.h>
#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;
}

5
sys/avr/lib/u8x8_avr.h Normal file
View File

@ -0,0 +1,5 @@
#include <u8g2.h>
#include <stdint.h>
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);