From 45df3148b054c78d5bd02cd3b2756c8f7df38778 Mon Sep 17 00:00:00 2001 From: servadmin Date: Tue, 2 Nov 2021 19:50:04 -0400 Subject: [PATCH] I2C refactored to c-periphery --- sys/arm-linux/README.md | 2 - sys/arm-linux/drivers/i2c.c | 164 +++++++++++++----- sys/arm-linux/drivers/i2c.h | 79 +++++++-- .../u8g2_4wire_hw_spi/u8g2_4wire_hw_spi.c | 4 +- .../c-examples/u8g2_hw_i2c/u8g2_hw_i2c.c | 2 + sys/arm-linux/port/u8g2port.c | 53 ++++-- sys/arm-linux/port/u8g2port.h | 17 +- 7 files changed, 244 insertions(+), 77 deletions(-) diff --git a/sys/arm-linux/README.md b/sys/arm-linux/README.md index c1034d79..18bd905e 100644 --- a/sys/arm-linux/README.md +++ b/sys/arm-linux/README.md @@ -1,5 +1,3 @@ -![Title](images/title.png) - [U8g2 for arm-linux](https://github.com/wuhanstudio/u8g2-arm-linux) has been modified to use [c-periphery](https://github.com/vsergeev/c-periphery) userspace library. * Deprecated sysfs method is no longer used for GPIO thus increasing speed and stability. diff --git a/sys/arm-linux/drivers/i2c.c b/sys/arm-linux/drivers/i2c.c index 7328a0d0..53929715 100644 --- a/sys/arm-linux/drivers/i2c.c +++ b/sys/arm-linux/drivers/i2c.c @@ -1,58 +1,136 @@ +/* + * c-periphery + * https://github.com/vsergeev/c-periphery + * License: MIT + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + #include "i2c.h" -int openI2CDevice(const char* device) -{ - int i2c_fd; - char filename[40]; - sprintf(filename, device); - if ((i2c_fd = open(filename,O_RDWR)) < 0) - { - printf("Failed to open the bus."); - /* ERROR HANDLING; you can check errno to see what went wrong */ - return -1; +struct i2c_handle { + int fd; + + struct { + int c_errno; + char errmsg[96]; + } error; +}; + +static int _i2c_error(i2c_t *i2c, int code, int c_errno, const char *fmt, ...) { + va_list ap; + + i2c->error.c_errno = c_errno; + + va_start(ap, fmt); + vsnprintf(i2c->error.errmsg, sizeof(i2c->error.errmsg), fmt, ap); + va_end(ap); + + /* Tack on strerror() and errno */ + if (c_errno) { + char buf[64]; + strerror_r(c_errno, buf, sizeof(buf)); + snprintf(i2c->error.errmsg+strlen(i2c->error.errmsg), sizeof(i2c->error.errmsg)-strlen(i2c->error.errmsg), ": %s [errno %d]", buf, c_errno); } - return i2c_fd; + + return code; } -void setI2CSlave(int i2c_fd,int addr) -{ - if (ioctl(i2c_fd, I2C_SLAVE, addr) < 0) - { - printf("Failed to acquire bus access and/or talk to slave.\n"); - /* ERROR HANDLING; you can check errno to see what went wrong */ - // exit(1); +i2c_t *i2c_new(void) { + i2c_t *i2c = calloc(1, sizeof(i2c_t)); + if (i2c == NULL) + return NULL; + + i2c->fd = -1; + + return i2c; +} + +void i2c_free(i2c_t *i2c) { + free(i2c); +} + +int i2c_open(i2c_t *i2c, const char *path) { + unsigned long supported_funcs; + + memset(i2c, 0, sizeof(i2c_t)); + + /* Open device */ + if ((i2c->fd = open(path, O_RDWR)) < 0) + return _i2c_error(i2c, I2C_ERROR_OPEN, errno, "Opening I2C device \"%s\"", path); + + /* Query supported functions */ + if (ioctl(i2c->fd, I2C_FUNCS, &supported_funcs) < 0) { + int errsv = errno; + close(i2c->fd); + i2c->fd = -1; + return _i2c_error(i2c, I2C_ERROR_QUERY, errsv, "Querying I2C functions"); } -} -void I2CWriteBytes(int i2c_fd, uint8_t* data, uint8_t length) -{ - if (write(i2c_fd, data, length) != length) - { - /* ERROR HANDLING: i2c transaction failed */ - printf("Failed to write to the i2c bus.\n"); + if (!(supported_funcs & I2C_FUNC_I2C)) { + close(i2c->fd); + i2c->fd = -1; + return _i2c_error(i2c, I2C_ERROR_NOT_SUPPORTED, 0, "I2C not supported on %s", path); } + + return 0; } -void sleep_ms(unsigned long milliseconds) -{ - struct timespec ts; - ts.tv_sec = milliseconds / 1000; - ts.tv_nsec = (milliseconds % 1000) * 1000000; - nanosleep(&ts, NULL); +int i2c_transfer(i2c_t *i2c, struct i2c_msg *msgs, size_t count) { + struct i2c_rdwr_ioctl_data i2c_rdwr_data; + + /* Prepare I2C transfer structure */ + memset(&i2c_rdwr_data, 0, sizeof(struct i2c_rdwr_ioctl_data)); + i2c_rdwr_data.msgs = msgs; + i2c_rdwr_data.nmsgs = count; + + /* Transfer */ + if (ioctl(i2c->fd, I2C_RDWR, &i2c_rdwr_data) < 0) + return _i2c_error(i2c, I2C_ERROR_TRANSFER, errno, "I2C transfer"); + + return 0; } -void sleep_us(unsigned long microseconds) -{ - struct timespec ts; - ts.tv_sec = microseconds / 1000 / 1000; - ts.tv_nsec = (microseconds % 1000000) * 1000; - nanosleep(&ts, NULL); +int i2c_close(i2c_t *i2c) { + if (i2c->fd < 0) + return 0; + + /* Close fd */ + if (close(i2c->fd) < 0) + return _i2c_error(i2c, I2C_ERROR_CLOSE, errno, "Closing I2C device"); + + i2c->fd = -1; + + return 0; } -void sleep_ns(unsigned long nanoseconds) -{ - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = nanoseconds; - nanosleep(&ts, NULL); +int i2c_tostring(i2c_t *i2c, char *str, size_t len) { + return snprintf(str, len, "I2C (fd=%d)", i2c->fd); } + +const char *i2c_errmsg(i2c_t *i2c) { + return i2c->error.errmsg; +} + +int i2c_errno(i2c_t *i2c) { + return i2c->error.c_errno; +} + +int i2c_fd(i2c_t *i2c) { + return i2c->fd; +} + diff --git a/sys/arm-linux/drivers/i2c.h b/sys/arm-linux/drivers/i2c.h index 3044d5c2..7383f5ea 100644 --- a/sys/arm-linux/drivers/i2c.h +++ b/sys/arm-linux/drivers/i2c.h @@ -1,21 +1,70 @@ -#ifndef I2C_H -#define I2C_H +/* + * c-periphery + * https://github.com/vsergeev/c-periphery + * License: MIT + */ -#include -#include +#ifndef _PERIPHERY_I2C_H +#define _PERIPHERY_I2C_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include #include -#include -#include -#include -#include -#include +#include -int openI2CDevice(const char* device); -void setI2CSlave(int i2c_fd,int addr); -void I2CWriteBytes(int i2c_fd, uint8_t* data, uint8_t length); +#include +#include -void sleep_ms(unsigned long milliseconds); -void sleep_us(unsigned long microseconds); -void sleep_ns(unsigned long nanoseconds); +enum i2c_error_code { + I2C_ERROR_ARG = -1, /* Invalid arguments */ + I2C_ERROR_OPEN = -2, /* Opening I2C device */ + I2C_ERROR_QUERY = -3, /* Querying I2C device attributes */ + I2C_ERROR_NOT_SUPPORTED = -4, /* I2C not supported on this device */ + I2C_ERROR_TRANSFER = -5, /* I2C transfer */ + I2C_ERROR_CLOSE = -6, /* Closing I2C device */ +}; + +typedef struct i2c_handle i2c_t; + +/* Primary Functions */ +i2c_t *i2c_new(void); +int i2c_open(i2c_t *i2c, const char *path); +int i2c_transfer(i2c_t *i2c, struct i2c_msg *msgs, size_t count); +int i2c_close(i2c_t *i2c); +void i2c_free(i2c_t *i2c); + +/* Miscellaneous */ +int i2c_fd(i2c_t *i2c); +int i2c_tostring(i2c_t *i2c, char *str, size_t len); + +/* Error Handling */ +int i2c_errno(i2c_t *i2c); +const char *i2c_errmsg(i2c_t *i2c); + +/* struct i2c_msg from : + + struct i2c_msg { + __u16 addr; + __u16 flags; + #define I2C_M_TEN 0x0010 + #define I2C_M_RD 0x0001 + #define I2C_M_STOP 0x8000 + #define I2C_M_NOSTART 0x4000 + #define I2C_M_REV_DIR_ADDR 0x2000 + #define I2C_M_IGNORE_NAK 0x1000 + #define I2C_M_NO_RD_ACK 0x0800 + #define I2C_M_RECV_LEN 0x0400 + __u16 len; + __u8 *buf; + }; +*/ + +#ifdef __cplusplus +} +#endif #endif + diff --git a/sys/arm-linux/examples/c-examples/u8g2_4wire_hw_spi/u8g2_4wire_hw_spi.c b/sys/arm-linux/examples/c-examples/u8g2_4wire_hw_spi/u8g2_4wire_hw_spi.c index 2cfa9f8c..965b964d 100644 --- a/sys/arm-linux/examples/c-examples/u8g2_4wire_hw_spi/u8g2_4wire_hw_spi.c +++ b/sys/arm-linux/examples/c-examples/u8g2_4wire_hw_spi/u8g2_4wire_hw_spi.c @@ -2,8 +2,8 @@ #include "u8g2port.h" // By default, SPI bus /dev/spidev0.0 is used, as defined in port/U8g2lib.h -#define OLED_SPI_PIN_RES 25 -#define OLED_SPI_PIN_DC 24 +#define OLED_SPI_PIN_RES 199 +#define OLED_SPI_PIN_DC 198 // CS pin is controlled by linux spi driver, thus not defined here, but need to be wired // #define OLED_SPI_PIN_CS 8 diff --git a/sys/arm-linux/examples/c-examples/u8g2_hw_i2c/u8g2_hw_i2c.c b/sys/arm-linux/examples/c-examples/u8g2_hw_i2c/u8g2_hw_i2c.c index 5c682dca..f14fd152 100644 --- a/sys/arm-linux/examples/c-examples/u8g2_hw_i2c/u8g2_hw_i2c.c +++ b/sys/arm-linux/examples/c-examples/u8g2_hw_i2c/u8g2_hw_i2c.c @@ -23,6 +23,8 @@ int main(void) u8g2_SendBuffer(&u8g2); printf("Initialized ...\n"); + sleep_ms(5000); + u8g2_SetPowerSave(&u8g2, 1); return 0; } diff --git a/sys/arm-linux/port/u8g2port.c b/sys/arm-linux/port/u8g2port.c index 0343a6d2..a605df2f 100644 --- a/sys/arm-linux/port/u8g2port.c +++ b/sys/arm-linux/port/u8g2port.c @@ -1,16 +1,42 @@ #include "u8g2port.h" -static int i2c_device; -static const char i2c_bus[] = "/dev/i2c-1"; +static i2c_t *i2c_device; +static const char i2c_bus[] = "/dev/i2c-0"; static int spi_device; -static const char spi_bus[] = "/dev/spidev0.0"; +static const char spi_bus[] = "/dev/spidev1.0"; +#if PERIPHERY_GPIO_CDEV_SUPPORT static const char gpio_device[] = "/dev/gpiochip0"; +#endif // c-periphery GPIO pins gpio_t *pins[U8X8_PIN_CNT] = { }; +void sleep_ms(unsigned long milliseconds) +{ + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + nanosleep(&ts, NULL); +} + +void sleep_us(unsigned long microseconds) +{ + struct timespec ts; + ts.tv_sec = microseconds / 1000 / 1000; + ts.tv_nsec = (microseconds % 1000000) * 1000; + nanosleep(&ts, NULL); +} + +void sleep_ns(unsigned long nanoseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = nanoseconds; + nanosleep(&ts, NULL); +} + /** * Initialize pin if not set to U8X8_PIN_NONE. */ @@ -21,13 +47,13 @@ void init_pin(u8x8_t *u8x8, int pin) { #if PERIPHERY_GPIO_CDEV_SUPPORT if (gpio_open(pins[pin], gpio_device, u8x8->pins[pin], GPIO_DIR_OUT_HIGH) < 0) { - fprintf(stderr, "gpio_open(): pin %d, %s\n", pin, gpio_errmsg(pins[pin])); + fprintf(stderr, "gpio_open(): pin %d, %s\n", u8x8->pins[pin], gpio_errmsg(pins[pin])); } // Support deprecated sysfs #else if (gpio_open_sysfs(pins[pin], u8x8->pins[pin], GPIO_DIR_OUT_HIGH) < 0) { - fprintf(stderr, "gpio_open_sysfs(): pin %d, %s\n", pin, + fprintf(stderr, "gpio_open_sysfs(): pin %d, %s\n", u8x8->pins[pin], gpio_errmsg(pins[pin])); } #endif @@ -197,7 +223,9 @@ uint8_t u8x8_byte_arm_linux_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */ static uint8_t buffer[32]; static uint8_t buf_idx; + static uint8_t addr; uint8_t *data; + struct i2c_msg msgs[1]; switch (msg) { case U8X8_MSG_BYTE_SEND: @@ -210,18 +238,23 @@ uint8_t u8x8_byte_arm_linux_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, break; case U8X8_MSG_BYTE_INIT: - i2c_device = openI2CDevice(i2c_bus); - // printf("I2C File Descriptor: %d\n", fd); + i2c_device = i2c_new(); + if (i2c_open(i2c_device, i2c_bus) < 0) { + fprintf(stderr, "i2c_open(): %s\n", i2c_errmsg(i2c_device)); + } + addr = u8x8_GetI2CAddress(u8x8) >> 1; break; case U8X8_MSG_BYTE_START_TRANSFER: - setI2CSlave(i2c_device, u8x8_GetI2CAddress(u8x8) >> 1); buf_idx = 0; - // printf("I2C Address: %02x\n", u8x8_GetI2CAddress(u8x8)>>1); break; case U8X8_MSG_BYTE_END_TRANSFER: - I2CWriteBytes(i2c_device, buffer, buf_idx); + msgs[0].addr = addr; + msgs[0].flags = 0; /* Write */ + msgs[0].len = buf_idx; + msgs[0].buf = buffer; + i2c_transfer(i2c_device, msgs, 1); break; default: diff --git a/sys/arm-linux/port/u8g2port.h b/sys/arm-linux/port/u8g2port.h index 7c05a757..72423179 100644 --- a/sys/arm-linux/port/u8g2port.h +++ b/sys/arm-linux/port/u8g2port.h @@ -2,23 +2,30 @@ #define U8G2LIB_H #ifdef __cplusplus - extern "C" { +extern "C" { #endif #include #include "gpio.h" #include "i2c.h" #include "spi.h" +#include +void sleep_ms(unsigned long milliseconds); +void sleep_us(unsigned long microseconds); +void sleep_ns(unsigned long nanoseconds); void init_pin(u8x8_t *u8x8, int pin); void done_pins(); void write_pin(u8x8_t *u8x8, int pin, int value); -uint8_t u8x8_arm_linux_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); -uint8_t u8x8_byte_arm_linux_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); -uint8_t u8x8_byte_arm_linux_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr); +uint8_t u8x8_arm_linux_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, + uint8_t arg_int, void *arg_ptr); +uint8_t u8x8_byte_arm_linux_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, + void *arg_ptr); +uint8_t u8x8_byte_arm_linux_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, + void *arg_ptr); #ifdef __cplusplus - } +} #endif #endif