I2C refactored to c-periphery

This commit is contained in:
servadmin 2021-11-02 19:50:04 -04:00
parent a7b8883bc3
commit 45df3148b0
7 changed files with 244 additions and 77 deletions

View File

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

View File

@ -1,58 +1,136 @@
/*
* c-periphery
* https://github.com/vsergeev/c-periphery
* License: MIT
*/
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#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;
}

View File

@ -1,21 +1,70 @@
#ifndef I2C_H
#define I2C_H
/*
* c-periphery
* https://github.com/vsergeev/c-periphery
* License: MIT
*/
#include <linux/i2c-dev.h>
#include <stdio.h>
#ifndef _PERIPHERY_I2C_H
#define _PERIPHERY_I2C_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <limits.h>
int openI2CDevice(const char* device);
void setI2CSlave(int i2c_fd,int addr);
void I2CWriteBytes(int i2c_fd, uint8_t* data, uint8_t length);
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
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 <linux/i2c.h>:
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

View File

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

View File

@ -23,6 +23,8 @@ int main(void)
u8g2_SendBuffer(&u8g2);
printf("Initialized ...\n");
sleep_ms(5000);
u8g2_SetPowerSave(&u8g2, 1);
return 0;
}

View File

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

View File

@ -2,23 +2,30 @@
#define U8G2LIB_H
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
#include <u8g2.h>
#include "gpio.h"
#include "i2c.h"
#include "spi.h"
#include <time.h>
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