3a04ac912b070cca470dbe1a77ecfc846e8b1bf3
[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
25 #define PCA9685_CH_MAX 16
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] = {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,
106                         LED0_ON_L + 4*channel, on & 0xFF);
107         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
108
109         ret = peripheral_i2c_write_register_byte(g_i2c_h,
110                         LED0_ON_H + 4*channel, on >> 8);
111         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
112
113         ret = peripheral_i2c_write_register_byte(g_i2c_h,
114                         LED0_OFF_L + 4*channel, off & 0xFF);
115         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
116
117         ret = peripheral_i2c_write_register_byte(g_i2c_h,
118                         LED0_OFF_H + 4*channel, off >> 8);
119         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
120
121         return 0;
122 }
123
124 static int resource_pca9685_set_value_to_all(int on, int off)
125 {
126         int ret = PERIPHERAL_ERROR_NONE;
127         retvm_if(g_i2c_h == NULL, -1, "Not initialized yet");
128
129         ret = peripheral_i2c_write_register_byte(g_i2c_h,
130                 ALL_LED_ON_L, on & 0xFF);
131         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
132
133         ret = peripheral_i2c_write_register_byte(g_i2c_h,
134                 ALL_LED_ON_H, on >> 8);
135         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
136
137         ret = peripheral_i2c_write_register_byte(g_i2c_h,
138                 ALL_LED_OFF_L, off & 0xFF);
139         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
140
141         ret = peripheral_i2c_write_register_byte(g_i2c_h,
142                 ALL_LED_OFF_H, off >> 8);
143         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
144
145         return 0;
146 }
147
148 int resource_pca9685_init(unsigned int ch)
149 {
150         uint8_t mode1 = 0;
151         int ret = PERIPHERAL_ERROR_NONE;
152
153         if (ch == 0 || ch >= PCA9685_CH_MAX) {
154                 _E("channel[%u] is out of range", ch);
155                 return -1;
156         }
157
158         if (ch_state[ch] == PCA9685_CH_STATE_USED) {
159                 _E("channel[%u] is already in used state", ch);
160                 return -1;
161         }
162
163         if (g_i2c_h)
164                 goto PASS_OPEN_HANDLE;
165
166         ret = peripheral_i2c_open(RPI3_I2C_BUS, PCA9685_ADDRESS, &g_i2c_h);
167         if (ret != PERIPHERAL_ERROR_NONE) {
168                 _E("failed to open pca9685-[bus:%d, addr:%d]",
169                         RPI3_I2C_BUS, PCA9685_ADDRESS);
170                 return -1;
171         }
172         ret = resource_pca9685_set_value_to_all(0, 0);
173         if (ret) {
174                 _E("failed to reset all value to register");
175                 goto ERROR;
176         }
177
178         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE2, OUTDRV);
179         if (ret != PERIPHERAL_ERROR_NONE) {
180                 _E("failed to write register");
181                 goto ERROR;
182         }
183
184         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, ALLCALL);
185         if (ret != PERIPHERAL_ERROR_NONE) {
186                 _E("failed to write register");
187                 goto ERROR;
188         }
189
190         usleep(500); // wait for oscillator
191
192         ret = peripheral_i2c_read_register_byte(g_i2c_h, MODE1, &mode1);
193         if (ret != PERIPHERAL_ERROR_NONE) {
194                 _E("failed to read register");
195                 goto ERROR;
196         }
197
198         mode1 = mode1 & (~SLEEP); // # wake up (reset sleep)
199         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, mode1);
200         if (ret != PERIPHERAL_ERROR_NONE) {
201                 _E("failed to write register");
202                 goto ERROR;
203         }
204
205         usleep(500); // wait for oscillator
206
207         ret = resource_pca9685_set_frequency(60);
208         if (ret) {
209                 _E("failed to set frequency");
210                 goto ERROR;
211         }
212
213 PASS_OPEN_HANDLE:
214         ref_count++;
215         ch_state[ch] = PCA9685_CH_STATE_USED;
216         _D("pca9685 - ref_count[%u]", ref_count);
217         _D("sets ch[%u] used state", ch);
218
219         return 0;
220
221 ERROR:
222         if (g_i2c_h)
223                 peripheral_i2c_close(g_i2c_h);
224
225         g_i2c_h = NULL;
226         return -1;
227 }
228
229 int resource_pca9685_fini(unsigned int ch)
230 {
231         if (ch_state[ch] == PCA9685_CH_STATE_NONE) {
232                 _E("channel[%u] is not in used state", ch);
233                 return -1;
234         }
235         resource_pca9685_set_value_to_channel(ch, 0, 0);
236         ch_state[ch] = PCA9685_CH_STATE_NONE;
237
238         ref_count--;
239         _D("ref count - %u", ref_count);
240
241         if (ref_count == 0 && g_i2c_h) {
242                 _D("finalizing pca9685");
243                 resource_pca9685_set_value_to_all(0, 0);
244                 peripheral_i2c_close(g_i2c_h);
245                 g_i2c_h = NULL;
246         }
247
248         return 0;
249 }