u8g2-copy/sys/arm-linux/port/u8g2port.c

490 lines
13 KiB
C

/*
* This code supports multiple displays since GPIO pin handles have been
* moved into user_data_struct. I2C and SPI handles are global since they can be
* shared by multiple devices (think I2C with different address sharing bus).
*
* So far I have tested this with 3 software I2C displays. See examples.
*/
#include "u8g2port.h"
// c-periphery I2C handles
static i2c_t *i2c_handles[MAX_I2C_HANDLES] = { NULL };
// c-periphery SPI handles
static spi_t *spi_handles[MAX_SPI_HANDLES] = { NULL };
/*
* Sleep milliseconds.
*/
void sleep_ms(unsigned long milliseconds) {
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
nanosleep(&ts, NULL);
}
/*
* Sleep microseconds.
*/
void sleep_us(unsigned long microseconds) {
struct timespec ts;
ts.tv_sec = microseconds / 1000 / 1000;
ts.tv_nsec = (microseconds % 1000000) * 1000;
nanosleep(&ts, NULL);
}
/**
* Sleep nanoseconds.
*/
void sleep_ns(unsigned long nanoseconds) {
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = nanoseconds;
nanosleep(&ts, NULL);
}
/*
* Allocate user_data_struct, set common values and set user_ptr.
*/
user_data_t* init_user_data(u8g2_t *u8g2) {
// Dynamically allocate user data_struct
user_data_t *user_data = (user_data_t*) malloc(sizeof(user_data_t));
// Dynamically allocate internal buffer
user_data->int_buf = (uint8_t*)malloc(u8g2_GetBufferSize(u8g2));
// We need a unique buffer for each display in order to be thread friendly
u8g2_SetBufferPtr(u8g2, user_data->int_buf);
for (int i = 0; i < U8X8_PIN_CNT; ++i) {
user_data->pins[i] = NULL;
}
u8g2_SetUserPtr(u8g2, user_data);
return user_data;
}
/*
* Allocate user_data_struct for I2C hardware.
*/
void init_i2c_hw(u8g2_t *u8g2, uint8_t bus) {
user_data_t *user_data = init_user_data(u8g2);
user_data->bus = bus;
}
/*
* Allocate user_data_struct for I2C software.
*/
void init_i2c_sw(u8g2_t *u8g2, uint8_t gpio_chip, uint8_t scl, uint8_t sda,
uint8_t res, unsigned long delay) {
user_data_t *user_data = init_user_data(u8g2);
user_data->gpio_chip = gpio_chip;
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_I2C_CLOCK, scl);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_I2C_DATA, sda);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_RESET, res);
user_data->delay = delay;
}
/*
* Allocate user_data_struct for hardware SPI advanced.
*/
void init_spi_hw_advanced(u8g2_t *u8g2, uint8_t gpio_chip, uint8_t bus,
uint8_t dc, uint8_t res, uint8_t cs, unsigned int spi_mode,
uint32_t max_speed) {
user_data_t *user_data = init_user_data(u8g2);
user_data->gpio_chip = gpio_chip;
user_data->bus = bus;
user_data->spi_mode = spi_mode;
user_data->max_speed = max_speed;
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_DC, dc);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_RESET, res);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_CS, cs);
}
/*
* Allocate user_data_struct for hardware SPI.
*/
void init_spi_hw(u8g2_t *u8g2, uint8_t gpio_chip, uint8_t bus, uint8_t dc,
uint8_t res, uint8_t cs) {
init_spi_hw_advanced(u8g2, gpio_chip, bus, dc, res, cs,
u8g2_GetU8x8(u8g2)->display_info->spi_mode, 500000);
}
/*
* Allocate user_data_struct for hardware SPI.
*/
void init_spi_sw(u8g2_t *u8g2, uint8_t gpio_chip, uint8_t dc, uint8_t res,
uint8_t mosi, uint8_t sck, uint8_t cs, unsigned long delay) {
user_data_t *user_data = init_user_data(u8g2);
user_data->gpio_chip = gpio_chip;
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_DC, dc);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_RESET, res);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_SPI_DATA, mosi);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_SPI_CLOCK, sck);
u8x8_SetPin(u8g2_GetU8x8(u8g2), U8X8_PIN_CS, cs);
user_data->delay = delay;
}
/*
* Close GPIO pins and free user_data_struct.
*/
void done_user_data(u8g2_t *u8g2) {
user_data_t *user_data = u8g2_GetUserPtr(u8g2);
if (user_data != NULL) {
// Close all GPIO pins
for (int i = 0; i < U8X8_PIN_CNT; ++i) {
if (user_data->pins[i] != NULL) {
gpio_close(user_data->pins[i]);
gpio_free(user_data->pins[i]);
}
}
// Free internal buffer
free(user_data->int_buf);
// Free user data struct
free(user_data);
u8g2_SetUserPtr(u8g2, NULL);
}
}
/**
* Initialize pin if not set to U8X8_PIN_NONE and NULL.
*/
#if PERIPHERY_GPIO_CDEV_SUPPORT
void init_pin(u8x8_t *u8x8, uint8_t pin) {
user_data_t *user_data = u8x8_GetUserPtr(u8x8);
char filename[16];
if (u8x8->pins[pin] != U8X8_PIN_NONE && user_data -> pins[pin] == NULL) {
snprintf(filename, sizeof(filename), "/dev/gpiochip%d", user_data ->gpio_chip);
user_data -> pins[pin] = gpio_new();
int error = gpio_open(user_data -> pins[pin], filename, u8x8->pins[pin], GPIO_DIR_OUT_HIGH);
if (error < 0) {
fprintf(stderr, "gpio_open(): pin %d, %s\n", u8x8->pins[pin],
gpio_errmsg(user_data -> pins[pin]));
gpio_free(user_data->pins[pin]);
}
}
}
#else
void init_pin(u8x8_t *u8x8, uint8_t pin) {
user_data_t *user_data = u8x8_GetUserPtr(u8x8);
if (u8x8->pins[pin] != U8X8_PIN_NONE && user_data->pins[pin] == NULL) {
user_data->pins[pin] = gpio_new();
int error = gpio_open_sysfs(user_data->pins[pin], u8x8->pins[pin],
GPIO_DIR_OUT_HIGH);
if (error < 0) {
fprintf(stderr, "gpio_open_sysfs(): pin %d, %s\n", u8x8->pins[pin],
gpio_errmsg(user_data->pins[pin]));
gpio_free(user_data->pins[pin]);
}
}
}
#endif
/**
* Write pin if not set to U8X8_PIN_NONE.
*/
void write_pin(u8x8_t *u8x8, uint8_t pin, uint8_t value) {
user_data_t *user_data = u8x8_GetUserPtr(u8x8);
if (u8x8->pins[pin] != U8X8_PIN_NONE) {
gpio_write(user_data->pins[pin], value);
}
}
/*
* Initialize I2C bus.
*/
void init_i2c(u8x8_t *u8x8) {
char filename[11];
user_data_t *user_data = u8x8_GetUserPtr(u8x8);
// Only open bus once
if (i2c_handles[user_data->bus] == NULL) {
snprintf(filename, sizeof(filename), "/dev/i2c-%d", user_data->bus);
i2c_handles[user_data->bus] = i2c_new();
int error = i2c_open(i2c_handles[user_data->bus], filename);
if (error) {
fprintf(stderr, "i2c_open(): %s\n",
i2c_errmsg(i2c_handles[user_data->bus]));
i2c_free(i2c_handles[user_data->bus]);
i2c_handles[user_data->bus] = NULL;
}
}
}
/*
* Close and free all i2c_t.
*/
void done_i2c() {
for (int i = 0; i < MAX_I2C_HANDLES; ++i) {
if (i2c_handles[i] != NULL) {
i2c_close(i2c_handles[i]);
i2c_free(i2c_handles[i]);
i2c_handles[i] = NULL;
}
}
}
/*
* Initialize SPI bus.
*/
void init_spi(u8x8_t *u8x8) {
char filename[15];
user_data_t *user_data = u8x8_GetUserPtr(u8x8);
// Only open bus once
if (spi_handles[user_data->bus] == NULL) {
// user_data->bus should be in the format 0x12 for /dev/spidev1.2
snprintf(filename, sizeof(filename), "/dev/spidev%d.%d",
user_data->bus >> 4, user_data->bus & 0x0f);
spi_handles[user_data->bus] = spi_new();
/* SPI mode has to be mapped to the mode of the current controller, at least Uno, Due, 101 have different SPI_MODEx values */
/* 0: clock active high, data out on falling edge, clock default value is zero, takover on rising edge */
/* 1: clock active high, data out on rising edge, clock default value is zero, takover on falling edge */
/* 2: clock active low, data out on rising edge */
/* 3: clock active low, data out on falling edge */
int error = spi_open(spi_handles[user_data->bus], filename,
user_data->spi_mode, user_data->max_speed);
if (error < 0) {
fprintf(stderr, "spi_open(): %s\n",
spi_errmsg(spi_handles[user_data->bus]));
spi_free(spi_handles[user_data->bus]);
spi_handles[user_data->bus] = NULL;
}
}
}
/*
* Close and free all spi_t.
*/
void done_spi() {
for (int i = 0; i < MAX_SPI_HANDLES; ++i) {
if (spi_handles[i] != NULL) {
spi_close(spi_handles[i]);
spi_free(spi_handles[i]);
spi_handles[i] = NULL;
}
}
}
/**
* GPIO callback.
*/
uint8_t u8x8_arm_linux_gpio_and_delay(u8x8_t *u8x8, uint8_t msg,
uint8_t arg_int, void *arg_ptr) {
user_data_t *user_data;
(void) arg_ptr; /* suppress unused parameter warning */
switch (msg) {
case U8X8_MSG_DELAY_NANO:
// delay arg_int * 1 nano second or 0 for none
user_data = u8x8_GetUserPtr(u8x8);
if (user_data->delay != 0) {
sleep_ns(user_data->delay);
}
break;
case U8X8_MSG_DELAY_100NANO:
// delay arg_int * 100 nano seconds
sleep_ns(arg_int * 100);
break;
case U8X8_MSG_DELAY_10MICRO:
// delay arg_int * 10 micro seconds
sleep_us(arg_int * 10);
break;
case U8X8_MSG_DELAY_MILLI:
// delay arg_int * 1 milli second
sleep_ms(arg_int);
break;
case U8X8_MSG_DELAY_I2C:
// arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz, but we ignore
// that and use user_data->delay
user_data = u8x8_GetUserPtr(u8x8);
if (user_data->delay != 0) {
sleep_ns(user_data->delay);
}
break;
case U8X8_MSG_GPIO_AND_DELAY_INIT:
// Function which implements a delay, arg_int contains the amount of ms
// SPI Pins
init_pin(u8x8, U8X8_PIN_SPI_CLOCK);
init_pin(u8x8, U8X8_PIN_SPI_DATA);
init_pin(u8x8, U8X8_PIN_CS);
init_pin(u8x8, U8X8_PIN_D2);
init_pin(u8x8, U8X8_PIN_D3);
init_pin(u8x8, U8X8_PIN_D4);
init_pin(u8x8, U8X8_PIN_D5);
init_pin(u8x8, U8X8_PIN_D6);
init_pin(u8x8, U8X8_PIN_D7);
init_pin(u8x8, U8X8_PIN_E);
init_pin(u8x8, U8X8_PIN_RESET);
init_pin(u8x8, U8X8_PIN_DC);
// I2c pins
init_pin(u8x8, U8X8_PIN_I2C_DATA);
init_pin(u8x8, U8X8_PIN_I2C_CLOCK);
break;
case U8X8_MSG_GPIO_D2:
// D2 pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_D2, arg_int);
break;
case U8X8_MSG_GPIO_D3:
// D3 pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_D3, arg_int);
break;
case U8X8_MSG_GPIO_D4:
// D4 pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_D4, arg_int);
break;
case U8X8_MSG_GPIO_D5:
// D5 pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_D5, arg_int);
break;
case U8X8_MSG_GPIO_D6:
// D6 pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_D6, arg_int);
break;
case U8X8_MSG_GPIO_D7:
// D7 pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_D7, arg_int);
break;
case U8X8_MSG_GPIO_E:
// E/WR pin: Output level in arg_int
write_pin(u8x8, U8X8_PIN_E, arg_int);
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
// arg_int=0: Output low at I2C clock pin
// arg_int=1: Input dir with pullup high for I2C clock pin
write_pin(u8x8, U8X8_PIN_I2C_CLOCK, arg_int);
break;
case U8X8_MSG_GPIO_I2C_DATA:
// arg_int=0: Output low at I2C data pin
// arg_int=1: Input dir with pullup high for I2C data pin
write_pin(u8x8, U8X8_PIN_I2C_DATA, arg_int);
break;
case U8X8_MSG_GPIO_SPI_CLOCK: // Same as U8X8_MSG_GPIO_D0
//Function to define the logic level of the clockline
write_pin(u8x8, U8X8_PIN_SPI_CLOCK, arg_int);
break;
case U8X8_MSG_GPIO_SPI_DATA: // Same as U8X8_MSG_GPIO_D1
//Function to define the logic level of the data line to the display
write_pin(u8x8, U8X8_PIN_SPI_DATA, arg_int);
break;
case U8X8_MSG_GPIO_CS:
// Function to define the logic level of the CS line
write_pin(u8x8, U8X8_PIN_CS, arg_int);
break;
case U8X8_MSG_GPIO_DC:
//Function to define the logic level of the Data/ Command line
write_pin(u8x8, U8X8_PIN_DC, arg_int);
break;
case U8X8_MSG_GPIO_RESET:
//Function to define the logic level of the RESET line
write_pin(u8x8, U8X8_PIN_RESET, arg_int);
break;
default:
return 0;
}
return 1;
}
/*
* I2c callback.
*/
uint8_t u8x8_byte_arm_linux_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,
void *arg_ptr) {
user_data_t *user_data;
uint8_t *data;
struct i2c_msg msgs[1];
switch (msg) {
case U8X8_MSG_BYTE_SEND:
user_data = u8x8_GetUserPtr(u8x8);
data = (uint8_t*) arg_ptr;
while (arg_int > 0) {
user_data->buffer[user_data->index++] = *data;
data++;
arg_int--;
}
break;
case U8X8_MSG_BYTE_INIT:
init_i2c(u8x8);
break;
case U8X8_MSG_BYTE_START_TRANSFER:
user_data = u8x8_GetUserPtr(u8x8);
user_data->index = 0;
break;
case U8X8_MSG_BYTE_END_TRANSFER:
user_data = u8x8_GetUserPtr(u8x8);
msgs[0].addr = u8x8_GetI2CAddress(u8x8) >> 1;
msgs[0].flags = 0; // Write
msgs[0].len = user_data->index;
msgs[0].buf = user_data->buffer;
i2c_transfer(i2c_handles[user_data->bus], msgs, 1);
break;
default:
return 0;
}
return 1;
}
/*
* SPI callback.
*/
uint8_t u8x8_byte_arm_linux_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,
void *arg_ptr) {
user_data_t *user_data;
uint8_t *data;
switch (msg) {
case U8X8_MSG_BYTE_SEND:
user_data = u8x8_GetUserPtr(u8x8);
user_data->index = 0;
data = (uint8_t*) arg_ptr;
while (arg_int > 0) {
user_data->buffer[user_data->index++] = *data;
data++;
arg_int--;
}
spi_transfer(spi_handles[user_data->bus], user_data->buffer,
user_data->buffer, user_data->index);
break;
case U8X8_MSG_BYTE_INIT:
init_spi(u8x8);
break;
case U8X8_MSG_BYTE_SET_DC:
u8x8_gpio_SetDC(u8x8, arg_int);
break;
case U8X8_MSG_BYTE_START_TRANSFER:
break;
case U8X8_MSG_BYTE_END_TRANSFER:
break;
default:
return 0;
}
return 1;
}