blob: b2dd237495ce80ac7e2063e75966de27a5c6ad6a [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <stdio.h>
#include <device.h>
#include <gpio.h>
#include <i2c.h>
#include <sys_clock.h>
#include <misc/util.h>
#define SLEEP_MSEC 200
#define GPIO_DATA_PIN 2
#define GPIO_CLK_PIN 3
#define GPIO_NAME "GPIO_SS_"
#define GPIO_DRV_NAME "GPIO_0"
#define APA102C_START_FRAME 0x00000000
#define APA102C_END_FRAME 0xFFFFFFFF
#define APA102C_BRIGHTNESS 0xE2000000
#define APA102C_BRIGHTNESS_MASK 0xFF000000
#define I2C_DRV_NAME "I2C_0"
#define APDS9960_ADDR 0x39
union rgbc_t {
u8_t raw[8];
struct {
u8_t cdatal;
u8_t cdatah;
u8_t rdatal;
u8_t rdatah;
u8_t gdatal;
u8_t gdatah;
u8_t bdatal;
u8_t bdatah;
} ch;
};
void apa102c_rgb_send(struct device *gpio_dev, u32_t rgb)
{
int i;
for (i = 0; i < 32; i++) {
/* MSB goes in first */
gpio_pin_write(gpio_dev, GPIO_DATA_PIN, !!(rgb & 0x80000000));
/* Latch data into LED */
gpio_pin_write(gpio_dev, GPIO_CLK_PIN, 1);
gpio_pin_write(gpio_dev, GPIO_CLK_PIN, 0);
rgb <<= 1;
}
}
void apa102c_led_program(struct device *gpio_dev, u32_t rgb)
{
apa102c_rgb_send(gpio_dev, APA102C_START_FRAME);
apa102c_rgb_send(gpio_dev, rgb);
apa102c_rgb_send(gpio_dev, APA102C_END_FRAME);
}
int apds9960_reg_write(struct device *i2c_dev,
u8_t reg_addr, u8_t reg_val)
{
struct i2c_msg msg;
u8_t data[2];
int ret;
msg.buf = data;
msg.flags = I2C_MSG_WRITE | I2C_MSG_STOP;
/* Enable Power (PON) so we can configure the sensor */
data[0] = reg_addr;
data[1] = reg_val;
msg.len = 2;
ret = i2c_transfer(i2c_dev, &msg, 1, APDS9960_ADDR);
if (ret) {
printf("Cannot write APDS9960 reg 0x%X to 0x%X\n",
reg_addr, reg_val);
}
return ret;
}
int apds9960_reg_read(struct device *i2c_dev, u8_t reg_addr,
u8_t *data, u8_t data_len)
{
struct i2c_msg msgs[2];
u8_t reg_data;
int ret;
/* Access RGBC data register */
reg_data = reg_addr;
msgs[0].buf = &reg_data;
msgs[0].len = 1;
msgs[0].flags = I2C_MSG_WRITE;
/* Read 8 bytes of RGBC values */
msgs[1].buf = data;
msgs[1].len = data_len;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_RESTART | I2C_MSG_STOP;
ret = i2c_transfer(i2c_dev, msgs, 2, APDS9960_ADDR);
if (ret) {
printf("Cannot read from APDS9960 reg 0x%X\n", reg_addr);
}
return ret;
}
void apds9960_setup(struct device *i2c_dev, int gain)
{
/* Enable Power (PON) so we can configure the sensor */
apds9960_reg_write(i2c_dev, 0x80, 0x01);
/* Max out the ADC values.
* So every ADC cycle is 200ms, and max
* ADC value is 65535.
*/
apds9960_reg_write(i2c_dev, 0x81, 0xB6);
/* ALS LEDs Gain */
apds9960_reg_write(i2c_dev, 0x8F, (gain & 0x03));
/* Enable Power (PON) and ALS*/
apds9960_reg_write(i2c_dev, 0x80, 0x03);
}
void apds9960_als_valid_wait(struct device *i2c_dev)
{
u8_t status;
while (1) {
apds9960_reg_read(i2c_dev, 0x93, &status, 1);
if (status & BIT(0)) {
break;
}
k_sleep(5);
}
}
void main(void)
{
struct device *gpio_dev, *i2c_dev;
int ret;
union rgbc_t rgbc;
int led_rgb;
int als_gain = 0;
gpio_dev = device_get_binding(GPIO_DRV_NAME);
if (!gpio_dev) {
printf("Cannot find %s!\n", GPIO_DRV_NAME);
return;
}
i2c_dev = device_get_binding(I2C_DRV_NAME);
if (!i2c_dev) {
printf("Cannot find %s!\n", I2C_DRV_NAME);
return;
}
/*
* Setup GPIO outputs
*/
ret = gpio_pin_configure(gpio_dev, GPIO_DATA_PIN, (GPIO_DIR_OUT));
if (ret) {
printf("Error configuring " GPIO_NAME "%d!\n", GPIO_DATA_PIN);
}
ret = gpio_pin_configure(gpio_dev, GPIO_CLK_PIN, (GPIO_DIR_OUT));
if (ret) {
printf("Error configuring " GPIO_NAME "%d!\n", GPIO_CLK_PIN);
}
/*
* Initialize the APDS9960 sensor with 1x ALS gain
*/
apds9960_setup(i2c_dev, als_gain);
while (1) {
apds9960_als_valid_wait(i2c_dev);
apds9960_reg_read(i2c_dev, 0x94, (u8_t *)&rgbc, 8);
/* Change the gain if it is too bright or too dark.
* Note there is no logic to prevent it from
* bouncing between two gain settings.
*/
if ((rgbc.ch.cdatah < 0x10) && (als_gain != 3)) {
/* More gain if too dark */
als_gain++;
if (als_gain > 3) {
als_gain = 3;
}
apds9960_setup(i2c_dev, als_gain);
printf("GAIN ==> %d\n", als_gain);
} else if ((rgbc.ch.cdatah > 0xEF) && (als_gain != 0)) {
/* Less gain if too bright */
als_gain--;
if (als_gain < 0) {
als_gain = 0;
}
apds9960_setup(i2c_dev, als_gain);
printf("GAIN ==> %d\n", als_gain);
} else {
/* Only program the LED when gain settles.
* Or else the LED would suddenly go all
* bright or dark before settling to
* the final value.
*/
led_rgb = (rgbc.ch.bdatah << 16)
+ (rgbc.ch.gdatah << 8)
+ (rgbc.ch.rdatah)
+ APA102C_BRIGHTNESS;
apa102c_led_program(gpio_dev, led_rgb);
}
}
}