add pca9685(pwm generator) api in resource module
[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 RPI3_I2C_BUS 1
26
27 /* Registers/etc: */
28 #define PCA9685_ADDRESS    0x40
29 #define MODE1              0x00
30 #define MODE2              0x01
31 #define SUBADR1            0x02
32 #define SUBADR2            0x03
33 #define SUBADR3            0x04
34 #define PRESCALE           0xFE
35 #define LED0_ON_L          0x06
36 #define LED0_ON_H          0x07
37 #define LED0_OFF_L         0x08
38 #define LED0_OFF_H         0x09
39 #define ALL_LED_ON_L       0xFA
40 #define ALL_LED_ON_H       0xFB
41 #define ALL_LED_OFF_L      0xFC
42 #define ALL_LED_OFF_H      0xFD
43
44 /* Bits: */
45 #define RESTART            0x80
46 #define SLEEP              0x10
47 #define ALLCALL            0x01
48 #define INVRT              0x10
49 #define OUTDRV             0x04
50
51 static peripheral_i2c_h g_i2c_h = NULL;
52 static unsigned int ref_count = 0;
53
54 int resource_pca9685_set_frequency(unsigned int freq_hz)
55 {
56         int ret = PERIPHERAL_ERROR_NONE;
57         double prescale_value = 0.0;
58         int prescale = 0;
59         uint8_t oldmode = 0;
60         uint8_t newmode = 0;
61
62         prescale_value = 25000000.0;    // 25MHz
63         prescale_value /= 4096.0;       // 12-bit
64         prescale_value /= (double)freq_hz;
65         prescale_value -= 1.0;
66
67         prescale = (int)floor(prescale_value + 0.5);
68
69         ret = peripheral_i2c_read_register_byte(g_i2c_h, MODE1, &oldmode);
70         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to read register");
71
72         newmode = (oldmode & 0x7F) | 0x10; // sleep
73         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, newmode); // go to sleep
74         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
75
76         ret = peripheral_i2c_write_register_byte(g_i2c_h, PRESCALE, prescale);
77         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
78
79         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, oldmode);
80         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
81
82         usleep(500);
83
84         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, (oldmode | 0x80));
85         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
86
87         return 0;
88 }
89
90 int resource_pca9685_set_value_to_channel(unsigned int channel, int on, int off)
91 {
92         int ret = PERIPHERAL_ERROR_NONE;
93         retvm_if(g_i2c_h == NULL, -1, "Not initialized yet");
94
95         ret = peripheral_i2c_write_register_byte(g_i2c_h,
96                         LED0_ON_L + 4*channel, on & 0xFF);
97         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
98
99         ret = peripheral_i2c_write_register_byte(g_i2c_h,
100                         LED0_ON_H + 4*channel, on >> 8);
101         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
102
103         ret = peripheral_i2c_write_register_byte(g_i2c_h,
104                         LED0_OFF_L + 4*channel, off & 0xFF);
105         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
106
107         ret = peripheral_i2c_write_register_byte(g_i2c_h,
108                         LED0_OFF_H + 4*channel, off >> 8);
109         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
110
111         return 0;
112 }
113
114 int resource_pca9685_set_value_to_all(int on, int off)
115 {
116         int ret = PERIPHERAL_ERROR_NONE;
117         retvm_if(g_i2c_h == NULL, -1, "Not initialized yet");
118
119         ret = peripheral_i2c_write_register_byte(g_i2c_h,
120                 ALL_LED_ON_L, on & 0xFF);
121         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
122
123         ret = peripheral_i2c_write_register_byte(g_i2c_h,
124                 ALL_LED_ON_H, on >> 8);
125         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
126
127         ret = peripheral_i2c_write_register_byte(g_i2c_h,
128                 ALL_LED_OFF_L, off & 0xFF);
129         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
130
131         ret = peripheral_i2c_write_register_byte(g_i2c_h,
132                 ALL_LED_OFF_H, off >> 8);
133         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
134
135         return 0;
136 }
137
138 int resource_pca9685_init(void)
139 {
140         uint8_t mode1 = 0;
141         int ret = PERIPHERAL_ERROR_NONE;
142
143         if (g_i2c_h) {
144                 ref_count++;
145                 _D("Already initialized - ref_count[%u]\n", ref_count);
146                 return 0;
147         }
148
149         ret = peripheral_i2c_open(RPI3_I2C_BUS, PCA9685_ADDRESS, &g_i2c_h);
150         if (ret != PERIPHERAL_ERROR_NONE) {
151                 _E("failed to open pca9685-[bus:%d, addr:%d]",
152                         RPI3_I2C_BUS, PCA9685_ADDRESS);
153                 return -1;
154         }
155         ret = resource_pca9685_set_value_to_all(0, 0);
156         retvm_if(ret, -1, "failed to set value to register");
157
158         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE2, OUTDRV);
159         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
160
161         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, ALLCALL);
162         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
163
164         usleep(500); // wait for oscillator
165
166         ret = peripheral_i2c_read_register_byte(g_i2c_h, MODE1, &mode1);
167         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to read register");
168
169         mode1 = mode1 & (~SLEEP); // # wake up (reset sleep)
170         ret = peripheral_i2c_write_register_byte(g_i2c_h, MODE1, mode1);
171         retvm_if(ret != PERIPHERAL_ERROR_NONE, -1, "failed to write register");
172
173         usleep(500); // wait for oscillator
174
175         ret = resource_pca9685_set_frequency(60);
176         if (ret) {
177                 _E("failed to set frequency");
178                 peripheral_i2c_close(g_i2c_h);
179                 g_i2c_h = NULL;
180                 return -1;
181         }
182         ref_count++;
183
184         return 0;
185 }
186
187 int resource_pca9685_fini(void)
188 {
189         ref_count--;
190
191         _D("ref count - %u", ref_count);
192
193         if (g_i2c_h) {
194                 _D("finalizing pca9685");
195                 resource_pca9685_set_value_to_all(0, 0);
196                 peripheral_i2c_close(g_i2c_h);
197                 g_i2c_h = NULL;
198         }
199
200         return 0;
201 }