drivers: Dev. spec. PWM functionality for SX1509B

Add device specific functions for initiating
and controlling PWM output pins.

Signed-off-by: Erik Robstad <erik.robstad@nordicsemi.no>
This commit is contained in:
Erik Robstad 2020-07-23 14:23:33 +02:00 committed by Carles Cufí
commit 1feb49fd22
8 changed files with 358 additions and 34 deletions

View file

@ -1,7 +1,7 @@
/*
* Copyright (c) 2018 Aapo Vienamo
* Copyright (c) 2018 Peter Bigot Consulting, LLC
* Copyright (c) 2019 Nordic Semiconductor ASA
* Copyright (c) 2019-2020 Nordic Semiconductor ASA
* Copyright (c) 2020 ZedBlox Ltd.
*
* SPDX-License-Identifier: Apache-2.0
@ -15,6 +15,7 @@
#include <device.h>
#include <init.h>
#include <drivers/gpio.h>
#include <drivers/gpio/gpio_sx1509b.h>
#include <drivers/i2c.h>
#include <sys/byteorder.h>
#include <sys/util.h>
@ -62,6 +63,7 @@ struct sx1509b_drv_data {
struct gpio_driver_data common;
const struct device *i2c_master;
struct sx1509b_pin_state pin_state;
uint16_t led_drv_enable;
struct sx1509b_debounce_state debounce_state;
struct k_sem lock;
@ -110,6 +112,14 @@ enum {
SX1509B_REG_CLOCK_FOSC_INT_2MHZ = 2 << 5,
};
/* Register bits for SX1509B_REG_MISC */
enum {
SX1509B_REG_MISC_LOG_A = 1 << 3,
SX1509B_REG_MISC_LOG_B = 1 << 7,
/* ClkX = fOSC */
SX1509B_REG_MISC_FREQ = 1 << 4,
};
/* Pin configuration register addresses */
enum {
SX1509B_REG_INPUT_DISABLE = 0x00,
@ -123,6 +133,8 @@ enum {
SX1509B_REG_INTERRUPT_SENSE_B = 0x14,
SX1509B_REG_INTERRUPT_SENSE_A = 0x16,
SX1509B_REG_INTERRUPT_SOURCE = 0x18,
SX1509B_REG_MISC = 0x1f,
SX1509B_REG_LED_DRV_ENABLE = 0x20,
SX1509B_REG_DEBOUNCE_CONFIG = 0x22,
SX1509B_REG_DEBOUNCE_ENABLE = 0x23,
};
@ -135,6 +147,12 @@ enum {
SX1509B_EDGE_BOTH = 0x03,
};
/* Intensity register addresses for all 16 pins */
static const uint8_t intensity_registers[16] = { 0x2a, 0x2d, 0x30, 0x33,
0x36, 0x3b, 0x40, 0x45,
0x4a, 0x4d, 0x50, 0x53,
0x56, 0x5b, 0x60, 0x65 };
/**
* @brief Write a big-endian word to an internal address of an I2C slave.
*
@ -230,6 +248,44 @@ static void sx1509_int_cb(const struct device *dev,
}
#endif
static int write_pin_state(const struct sx1509b_config *cfg,
struct sx1509b_drv_data *drv_data,
struct sx1509b_pin_state *pins, bool data_first)
{
struct {
uint8_t reg;
struct sx1509b_pin_state pins;
} __packed pin_buf;
int rc;
pin_buf.reg = SX1509B_REG_INPUT_DISABLE;
pin_buf.pins.input_disable = sys_cpu_to_be16(pins->input_disable);
pin_buf.pins.long_slew = sys_cpu_to_be16(pins->long_slew);
pin_buf.pins.low_drive = sys_cpu_to_be16(pins->low_drive);
pin_buf.pins.pull_up = sys_cpu_to_be16(pins->pull_up);
pin_buf.pins.pull_down = sys_cpu_to_be16(pins->pull_down);
pin_buf.pins.open_drain = sys_cpu_to_be16(pins->open_drain);
pin_buf.pins.polarity = sys_cpu_to_be16(pins->polarity);
pin_buf.pins.dir = sys_cpu_to_be16(pins->dir);
pin_buf.pins.data = sys_cpu_to_be16(pins->data);
if (data_first) {
rc = i2c_reg_write_word_be(drv_data->i2c_master,
cfg->i2c_slave_addr,
SX1509B_REG_DATA, pins->data);
if (rc == 0) {
rc = i2c_write(drv_data->i2c_master, &pin_buf.reg,
sizeof(pin_buf) - sizeof(pins->data),
cfg->i2c_slave_addr);
}
} else {
rc = i2c_write(drv_data->i2c_master, &pin_buf.reg,
sizeof(pin_buf), cfg->i2c_slave_addr);
}
return rc;
}
static int sx1509b_config(const struct device *dev,
gpio_pin_t pin,
gpio_flags_t flags)
@ -237,10 +293,6 @@ static int sx1509b_config(const struct device *dev,
const struct sx1509b_config *cfg = dev->config;
struct sx1509b_drv_data *drv_data = dev->data;
struct sx1509b_pin_state *pins = &drv_data->pin_state;
struct {
uint8_t reg;
struct sx1509b_pin_state pins;
} __packed pin_buf;
struct sx1509b_debounce_state *debounce = &drv_data->debounce_state;
int rc = 0;
bool data_first = false;
@ -267,6 +319,19 @@ static int sx1509b_config(const struct device *dev,
k_sem_take(&drv_data->lock, K_FOREVER);
if (drv_data->led_drv_enable & BIT(pin)) {
/* Disable LED driver */
drv_data->led_drv_enable &= ~BIT(pin);
rc = i2c_reg_write_word_be(drv_data->i2c_master,
cfg->i2c_slave_addr,
SX1509B_REG_LED_DRV_ENABLE,
drv_data->led_drv_enable);
if (rc) {
goto out;
}
}
pins->open_drain &= ~BIT(pin);
if ((flags & GPIO_SINGLE_ENDED) != 0) {
if ((flags & GPIO_LINE_OPEN_DRAIN) != 0) {
@ -314,37 +379,12 @@ static int sx1509b_config(const struct device *dev,
debounce->debounce_enable &= ~BIT(pin);
}
pin_buf.reg = SX1509B_REG_INPUT_DISABLE;
pin_buf.pins.input_disable = sys_cpu_to_be16(pins->input_disable);
pin_buf.pins.long_slew = sys_cpu_to_be16(pins->long_slew);
pin_buf.pins.low_drive = sys_cpu_to_be16(pins->low_drive);
pin_buf.pins.pull_up = sys_cpu_to_be16(pins->pull_up);
pin_buf.pins.pull_down = sys_cpu_to_be16(pins->pull_down);
pin_buf.pins.open_drain = sys_cpu_to_be16(pins->open_drain);
pin_buf.pins.polarity = sys_cpu_to_be16(pins->polarity);
pin_buf.pins.dir = sys_cpu_to_be16(pins->dir);
pin_buf.pins.data = sys_cpu_to_be16(pins->data);
LOG_DBG("CFG %u %x : ID %04x ; PU %04x ; PD %04x ; DIR %04x ; DAT %04x",
pin, flags,
pins->input_disable, pins->pull_up, pins->pull_down,
pins->dir, pins->data);
if (data_first) {
rc = i2c_reg_write_word_be(drv_data->i2c_master,
cfg->i2c_slave_addr,
SX1509B_REG_DATA, pins->data);
if (rc == 0) {
rc = i2c_write(drv_data->i2c_master,
&pin_buf.reg,
sizeof(pin_buf) - sizeof(pins->data),
cfg->i2c_slave_addr);
}
} else {
rc = i2c_write(drv_data->i2c_master,
&pin_buf.reg,
sizeof(pin_buf),
cfg->i2c_slave_addr);
}
rc = write_pin_state(cfg, drv_data, pins, data_first);
if (rc == 0) {
struct {
@ -611,8 +651,12 @@ static int sx1509b_init(const struct device *dev)
SX1509B_REG_DIR,
drv_data->pin_state.dir);
}
if (rc != 0) {
goto out;
if (rc == 0) {
rc = i2c_reg_write_byte_be(
drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_MISC,
SX1509B_REG_MISC_LOG_A | SX1509B_REG_MISC_LOG_B |
SX1509B_REG_MISC_FREQ);
}
out:
@ -649,6 +693,80 @@ static const struct gpio_driver_api api_table = {
#endif
};
int sx1509b_led_intensity_pin_configure(const struct device *dev,
gpio_pin_t pin)
{
const struct sx1509b_config *cfg = dev->config;
struct sx1509b_drv_data *drv_data = dev->data;
struct sx1509b_pin_state *pins = &drv_data->pin_state;
int rc;
/* Can't do I2C bus operations from an ISR */
if (k_is_in_isr()) {
return -EWOULDBLOCK;
}
if (pin >= ARRAY_SIZE(intensity_registers)) {
return -ERANGE;
}
k_sem_take(&drv_data->lock, K_FOREVER);
/* Enable LED driver */
drv_data->led_drv_enable |= BIT(pin);
rc = i2c_reg_write_word_be(drv_data->i2c_master, cfg->i2c_slave_addr,
SX1509B_REG_LED_DRV_ENABLE,
drv_data->led_drv_enable);
/* Set intensity to 0 */
if (rc == 0) {
rc = i2c_reg_write_byte_be(drv_data->i2c_master,
cfg->i2c_slave_addr,
intensity_registers[pin], 0);
} else {
goto out;
}
pins->input_disable |= BIT(pin);
pins->pull_up &= ~BIT(pin);
pins->dir &= ~BIT(pin);
pins->data &= ~BIT(pin);
if (rc == 0) {
rc = write_pin_state(cfg, drv_data, pins, false);
}
out:
k_sem_give(&drv_data->lock);
return rc;
}
int sx1509b_led_intensity_pin_set(const struct device *dev, gpio_pin_t pin,
uint8_t intensity_val)
{
const struct sx1509b_config *cfg = dev->config;
struct sx1509b_drv_data *drv_data = dev->data;
int rc;
/* Can't do I2C bus operations from an ISR */
if (k_is_in_isr()) {
return -EWOULDBLOCK;
}
if (pin >= ARRAY_SIZE(intensity_registers)) {
return -ERANGE;
}
k_sem_take(&drv_data->lock, K_FOREVER);
rc = i2c_reg_write_byte_be(drv_data->i2c_master, cfg->i2c_slave_addr,
intensity_registers[pin], intensity_val);
k_sem_give(&drv_data->lock);
return rc;
}
static const struct sx1509b_config sx1509b_cfg = {
.common = {
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(0),

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_GPIO_GPIO_SX1509B_H_
#define ZEPHYR_INCLUDE_DRIVERS_GPIO_GPIO_SX1509B_H_
#include <device.h>
#include <drivers/gpio.h>
/**
* @brief Configure a pin for LED intensity.
*
* Configure a pin to be controlled by SX1509B LED driver using
* the LED intensity functionality.
* To get back normal GPIO funcionality, configure the pin using
* the standard GPIO API.
*
* @param dev Pointer to the device structure for the driver instance.
* @param pin Pin number.
*
* @retval 0 If successful.
* @retval -EWOULDBLOCK if function is called from an ISR.
* @retval -ERANGE if pin number is out of range.
* @retval -EIO if I2C fails.
*/
int sx1509b_led_intensity_pin_configure(const struct device *dev,
gpio_pin_t pin);
/**
* @brief Set LED intensity of selected pin.
*
* @param dev Pointer to the device structure for the driver instance.
* @param pin Pin number.
* @param intensity_val Intensity value.
*
* @retval 0 If successful.
* @retval -EIO if I2C fails.
*/
int sx1509b_led_intensity_pin_set(const struct device *dev, gpio_pin_t pin,
uint8_t intensity_val);
#endif /* ZEPHYR_INCLUDE_DRIVERS_GPIO_GPIO_SX1509B_H_ */

View file

@ -0,0 +1,13 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(led_sx1509b_intensity)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,21 @@
.. _sx1509b_intensity:
SX1509B LED Intensity
######################
Overview
********
This sample controls the intensity of the color LEDs in a Thingy:52 lightwell.
The red, green and blue LED fade up one by one, and this repeats forever.
Building and Running
********************
.. zephyr-app-commands::
:zephyr-app: samples/drivers/led_sx1509b_intensity
:board: thingy52_nrf52832
:goals: build flash
:compact:
The log is configured to output to RTT. Segger J-Link RTT Viewer can be used to view the log.

View file

@ -0,0 +1,8 @@
/* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
&sx1509b {
status = "okay";
};

View file

@ -0,0 +1,12 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_MINIMAL=n
CONFIG_LOG_PRINTK=y
CONFIG_I2C=y

View file

@ -0,0 +1,14 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
sample:
description: Demonstration of the SX1509B LED driver intensity functionality
name: SX1509B intensity sample
tests:
sample.drivers.led.sx1509b_intensity:
platform_allow: thingy52_nrf52832
tags: LED
depends_on: i2c

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/gpio.h>
#include <drivers/gpio/gpio_sx1509b.h>
#define NUMBER_OF_LEDS 3
#define GREEN_LED DT_GPIO_PIN(DT_NODELABEL(led0), gpios)
#define BLUE_LED DT_GPIO_PIN(DT_NODELABEL(led1), gpios)
#define RED_LED DT_GPIO_PIN(DT_NODELABEL(led2), gpios)
enum sx1509b_color { sx1509b_red = 0, sx1509b_green, sx1509b_blue };
const struct device *sx1509b_dev;
static struct k_delayed_work rgb_work;
static uint8_t which_color = sx1509b_red;
static uint8_t iterator;
static const gpio_pin_t rgb_pins[] = {
RED_LED,
GREEN_LED,
BLUE_LED,
};
static void rgb_work_handler(struct k_work *work)
{
if (which_color == sx1509b_red) {
sx1509b_led_intensity_pin_set(sx1509b_dev, RED_LED, iterator);
iterator++;
if (iterator >= 255) {
sx1509b_led_intensity_pin_set(sx1509b_dev, RED_LED, 0);
which_color = sx1509b_green;
iterator = 0;
}
} else if (which_color == sx1509b_green) {
sx1509b_led_intensity_pin_set(sx1509b_dev, GREEN_LED, iterator);
iterator++;
if (iterator >= 255) {
sx1509b_led_intensity_pin_set(sx1509b_dev, GREEN_LED,
0);
which_color = sx1509b_blue;
iterator = 0;
}
} else if (which_color == sx1509b_blue) {
sx1509b_led_intensity_pin_set(sx1509b_dev, BLUE_LED, iterator);
iterator++;
if (iterator >= 255) {
sx1509b_led_intensity_pin_set(sx1509b_dev, BLUE_LED, 0);
which_color = sx1509b_red;
iterator = 0;
}
}
k_delayed_work_submit(&rgb_work, K_MSEC(10));
}
void main(void)
{
int err;
printk("SX1509B intensity sample\n");
sx1509b_dev = device_get_binding(DT_PROP(DT_NODELABEL(sx1509b), label));
if (sx1509b_dev == NULL) {
printk("Error binding SX1509B device\n");
return;
}
for (int i = 0; i < NUMBER_OF_LEDS; i++) {
err = sx1509b_led_intensity_pin_configure(sx1509b_dev,
rgb_pins[i]);
if (err) {
printk("Error configuring pin for LED intensity\n");
}
}
k_delayed_work_init(&rgb_work, rgb_work_handler);
k_delayed_work_submit(&rgb_work, K_NO_WAIT);
}