I2C refactored to c-periphery
This commit is contained in:
parent
a7b8883bc3
commit
45df3148b0
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,6 +23,8 @@ int main(void)
|
|||
u8g2_SendBuffer(&u8g2);
|
||||
|
||||
printf("Initialized ...\n");
|
||||
sleep_ms(5000);
|
||||
u8g2_SetPowerSave(&u8g2, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue