I2c led
[apps/native/gear-racing-car.git] / src / resource / resource_PCA9685.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Contact: Jeonghoon Park <jh1979.park@samsung.com>
5  *
6  * Licensed under the Flora License, Version 1.1 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://floralicense.org/license/
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an AS IS BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <math.h>
22 #include <peripheral_io.h>
23 #include "log.h"
24 #include "resource/resource_PCA9685.h"
25
26 #define RPI3_I2C_BUS 1
27
28 /* Registers/etc: */
29 #define PCA9685_ADDRESS    0x40
30 #define MODE1              0x00
31 #define MODE2              0x01
32 #define SUBADR1            0x02
33 #define SUBADR2            0x03
34 #define SUBADR3            0x04
35 #define PRESCALE           0xFE
36 #define LED0_ON_L          0x06
37 #define LED0_ON_H          0x07
38 #define LED0_OFF_L         0x08
39 #define LED0_OFF_H         0x09
40 #define ALL_LED_ON_L       0xFA
41 #define ALL_LED_ON_H       0xFB
42 #define ALL_LED_OFF_L      0xFC
43 #define ALL_LED_OFF_H      0xFD
44
45 /* Bits: */
46 #define RESTART            0x80
47 #define SLEEP              0x10
48 #define ALLCALL            0x01
49 #define INVRT              0x10
50 #define OUTDRV             0x04
51
52 typedef enum {
53         PCA9685_CH_STATE_NONE,
54         PCA9685_CH_STATE_USED,
55 } pca9685_ch_state_e;
56
57 static peripheral_i2c_h g_i2c_h = NULL;
58 static unsigned int ref_count = 0;
59 static pca9685_ch_state_e ch_state[PCA9685_CH_MAX + 1] = {PCA9685_CH_STATE_NONE, };
60
61 int resource_pca9685_set_frequency(unsigned int freq_hz)
62 {
63         int ret = PERIPHERAL_ERROR_NONE;
64         double prescale_value = 0.0;
65         int prescale = 0;
66         uint8_t oldmode = 0;
67         uint8_t newmode = 0;
68
69         prescale_value = 25000000.0;    // 25MHz
70         prescale_value /= 4096.0;       // 12-bit
71         prescale_value /= (double)freq_hz;
72         prescale_value -= 1.0;
73
74         prescale = (int)floor(prescale_value + 0.5);
75
76         ret = peripheral_i2c_read_register_byte(g_i2c_h, MODE1, &oldmode);
77         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to read register");
78
79         newmode = (oldmode & 0x7F) | 0x10; // sleep
80         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, newmode); // go to sleep
81         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
82
83         ret = peripheral_i2c_write_register_byte(g_i2c_h, PRESCALE, prescale);
84         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
85
86         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, oldmode);
87         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
88
89         usleep(500);
90
91         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, (oldmode | 0x80));
92         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
93
94         return 0;
95 }
96
97 int resource_pca9685_set_value_to_channel(unsigned int channel, int on, int off)
98 {
99         int ret = PERIPHERAL_ERROR_NONE;
100         retvm_if(g_i2c_h == NULL, -1, "Not initialized yet");
101
102         retvm_if(ch_state[channel] == PCA9685_CH_STATE_NONE, -1,
103                 "ch[%u] is not in used state", channel);
104
105         ret = peripheral_i2c_write_register_byte(g_i2c_h, LED0_ON_L + 4*channel, on & 0xFF);
106         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
107
108         ret = peripheral_i2c_write_register_byte(g_i2c_h, LED0_ON_H + 4*channel, on >> 8);
109         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
110
111         ret = peripheral_i2c_write_register_byte(g_i2c_h, LED0_OFF_L + 4*channel, off & 0xFF);
112         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
113
114         ret = peripheral_i2c_write_register_byte(g_i2c_h, LED0_OFF_H + 4*channel, off >> 8);
115         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
116
117         return 0;
118 }
119
120 static int resource_pca9685_set_value_to_all(int on, int off)
121 {
122         int ret = PERIPHERAL_ERROR_NONE;
123         retvm_if(g_i2c_h == NULL, -1, "Not initialized yet");
124
125         ret = peripheral_i2c_write_register_byte(g_i2c_h, ALL_LED_ON_L, on & 0xFF);
126         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
127
128         ret = peripheral_i2c_write_register_byte(g_i2c_h, ALL_LED_ON_H, on >> 8);
129         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
130
131         ret = peripheral_i2c_write_register_byte(g_i2c_h, ALL_LED_OFF_L, off & 0xFF);
132         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
133
134         ret = peripheral_i2c_write_register_byte(g_i2c_h, ALL_LED_OFF_H, off >> 8);
135         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
136
137         return 0;
138 }
139
140 int resource_pca9685_init(unsigned int ch, unsigned int freq)
141 {
142         uint8_t mode1 = 0;
143         int ret = PERIPHERAL_ERROR_NONE;
144
145         if (ch > PCA9685_CH_MAX) {
146                 _E("channel[%u] is out of range", ch);
147                 return -1;
148         }
149
150         if (ch_state[ch] == PCA9685_CH_STATE_USED) {
151                 _E("channel[%u] is already in used state", ch);
152                 return -1;
153         }
154
155         if (g_i2c_h)
156                 goto PASS_OPEN_HANDLE;
157
158         ret = peripheral_i2c_open(RPI3_I2C_BUS, PCA9685_ADDRESS, &g_i2c_h);
159         if (ret != PERIPHERAL_ERROR_NONE) {
160                 _E("failed to open pca9685-[bus:%d, addr:%d]", RPI3_I2C_BUS, PCA9685_ADDRESS);
161                 return -1;
162         }
163         ret = resource_pca9685_set_value_to_all(0, 0);
164         if (ret) {
165                 _E("failed to reset all value to register");
166                 goto ERROR;
167         }
168
169         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE2, OUTDRV);
170         if (ret != PERIPHERAL_ERROR_NONE) {
171                 _E("failed to write register");
172                 goto ERROR;
173         }
174
175         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, ALLCALL);
176         if (ret != PERIPHERAL_ERROR_NONE) {
177                 _E("failed to write register");
178                 goto ERROR;
179         }
180
181         usleep(500); // wait for oscillator
182
183         ret = peripheral_i2c_read_register_byte(g_i2c_h, MODE1, &mode1);
184         if (ret != PERIPHERAL_ERROR_NONE) {
185                 _E("failed to read register");
186                 goto ERROR;
187         }
188
189         mode1 = mode1 & (~SLEEP); // # wake up (reset sleep)
190         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, mode1);
191         if (ret != PERIPHERAL_ERROR_NONE) {
192                 _E("failed to write register");
193                 goto ERROR;
194         }
195
196         usleep(500); // wait for oscillator
197
198         ret = resource_pca9685_set_frequency(freq);
199         if (ret) {
200                 _E("failed to set frequency");
201                 goto ERROR;
202         }
203
204 PASS_OPEN_HANDLE:
205         ref_count++;
206         ch_state[ch] = PCA9685_CH_STATE_USED;
207         _D("pca9685 - ref_count[%u]", ref_count);
208         _D("sets ch[%u] used state", ch);
209
210         return 0;
211
212 ERROR:
213         if (g_i2c_h)
214                 peripheral_i2c_close(g_i2c_h);
215
216         g_i2c_h = NULL;
217         return -1;
218 }
219
220 int resource_pca9685_fini(unsigned int ch)
221 {
222         if (ch_state[ch] == PCA9685_CH_STATE_NONE) {
223                 _E("channel[%u] is not in used state", ch);
224                 return -1;
225         }
226         resource_pca9685_set_value_to_channel(ch, 0, 0);
227         ch_state[ch] = PCA9685_CH_STATE_NONE;
228
229         ref_count--;
230         _D("ref count - %u", ref_count);
231
232         if (ref_count == 0 && g_i2c_h) {
233                 _D("finalizing pca9685");
234                 resource_pca9685_set_value_to_all(0, 0);
235                 peripheral_i2c_close(g_i2c_h);
236                 g_i2c_h = NULL;
237         }
238
239         return 0;
240 }