forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpwm_pca9685.c
170 lines (140 loc) · 3.99 KB
/
pwm_pca9685.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
* Copyright (c) 2015 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file Driver for PCA9685 I2C-based PWM driver.
*/
#include <errno.h>
#include <kernel.h>
#include <i2c.h>
#include <pwm.h>
#include "pwm_pca9685.h"
#define REG_MODE1 0x00
#define REG_MODE2 0x01
#define REG_LED_ON_L(n) ((4 * n) + 0x06)
#define REG_LED_ON_H(n) ((4 * n) + 0x07)
#define REG_LED_OFF_L(n) ((4 * n) + 0x08)
#define REG_LED_OFF_H(n) ((4 * n) + 0x09)
#define REG_ALL_LED_ON_L 0xFA
#define REG_ALL_LED_ON_H 0xFB
#define REG_ALL_LED_OFF_L 0xFC
#define REG_ALL_LED_OFF_H 0xFD
#define REG_PRE_SCALE 0xFE
/* Maximum PWM outputs */
#define MAX_PWM_OUT 16
/* How many ticks per one period */
#define PWM_ONE_PERIOD_TICKS 4096
/**
* @brief Check to see if a I2C master is identified for communication.
*
* @param dev Device struct.
* @return 1 if I2C master is identified, 0 if not.
*/
static inline int _has_i2c_master(struct device *dev)
{
struct pwm_pca9685_drv_data * const drv_data =
(struct pwm_pca9685_drv_data * const)dev->driver_data;
struct device * const i2c_master = drv_data->i2c_master;
if (i2c_master)
return 1;
else
return 0;
}
/*
* period_count is always taken as 4095. To control the on period send
* value to pulse_count
*/
static int pwm_pca9685_pin_set_cycles(struct device *dev, u32_t pwm,
u32_t period_count, u32_t pulse_count)
{
const struct pwm_pca9685_config * const config =
dev->config->config_info;
struct pwm_pca9685_drv_data * const drv_data =
(struct pwm_pca9685_drv_data * const)dev->driver_data;
struct device * const i2c_master = drv_data->i2c_master;
u16_t i2c_addr = config->i2c_slave_addr;
u8_t buf[] = { 0, 0, 0, 0, 0};
ARG_UNUSED(period_count);
if (!_has_i2c_master(dev)) {
return -EINVAL;
}
if (pwm > MAX_PWM_OUT) {
return -EINVAL;
}
buf[0] = REG_LED_ON_L(pwm);
/* If either pulse_count > max ticks, treat PWM as 100%.
* If pulse_count value == 0, treat it as 0%.
* Otherwise, populate registers accordingly.
*/
if (pulse_count >= PWM_ONE_PERIOD_TICKS) {
buf[1] = 0x0;
buf[2] = (1 << 4);
buf[3] = 0x0;
buf[4] = 0x0;
} else if (pulse_count == 0) {
buf[1] = 0x0;
buf[2] = 0x0;
buf[3] = 0x0;
buf[4] = (1 << 4);
} else {
/* No start delay given. When the count is 0 the
* pwm will be high .
*/
buf[0] = 0x0;
buf[1] = 0x0;
buf[2] = (pulse_count & 0xFF);
buf[3] = ((pulse_count >> 8) & 0x0F);
}
return i2c_write(i2c_master, buf, sizeof(buf), i2c_addr);
}
static const struct pwm_driver_api pwm_pca9685_drv_api_funcs = {
.pin_set = pwm_pca9685_pin_set_cycles
};
/**
* @brief Initialization function of PCA9685
*
* @param dev Device struct
* @return 0 if successful, failed otherwise.
*/
int pwm_pca9685_init(struct device *dev)
{
const struct pwm_pca9685_config * const config =
dev->config->config_info;
struct pwm_pca9685_drv_data * const drv_data =
(struct pwm_pca9685_drv_data * const)dev->driver_data;
struct device *i2c_master;
u8_t buf[] = {0, 0};
int ret;
/* Find out the device struct of the I2C master */
i2c_master = device_get_binding((char *)config->i2c_master_dev_name);
if (!i2c_master) {
return -EINVAL;
}
drv_data->i2c_master = i2c_master;
/* MODE1 register */
buf[0] = REG_MODE1;
buf[1] = (1 << 5); /* register addr auto increment */
ret = i2c_write(i2c_master, buf, 2, config->i2c_slave_addr);
if (ret != 0) {
return -EPERM;
}
return 0;
}
/* Initialization for PWM_PCA9685_0 */
#ifdef CONFIG_PWM_PCA9685_0
#include <device.h>
#include <init.h>
static const struct pwm_pca9685_config pwm_pca9685_0_cfg = {
.i2c_master_dev_name = CONFIG_PWM_PCA9685_0_I2C_MASTER_DEV_NAME,
.i2c_slave_addr = CONFIG_PWM_PCA9685_0_I2C_ADDR,
};
static struct pwm_pca9685_drv_data pwm_pca9685_0_drvdata;
/* This has to init after I2C master */
DEVICE_AND_API_INIT(pwm_pca9685_0, CONFIG_PWM_PCA9685_0_DEV_NAME,
pwm_pca9685_init,
&pwm_pca9685_0_drvdata, &pwm_pca9685_0_cfg,
POST_KERNEL, CONFIG_PWM_PCA9685_INIT_PRIORITY,
&pwm_pca9685_drv_api_funcs);
#endif /* CONFIG_PWM_PCA9685_0 */