Contiki 3.x
pwm-pca9685.c
1 /*
2  * Copyright (C) 2015, Intel Corporation. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "i2c.h"
32 #include "pwm-pca9685.h"
33 
34 #define REG_MODE1 0x00
35 #define REG_MODE2 0x01
36 
37 #define REG_LED_ON_L(n) ((4 * n) + 0x06)
38 #define REG_LED_ON_H(n) ((4 * n) + 0x07)
39 #define REG_LED_OFF_L(n) ((4 * n) + 0x08)
40 #define REG_LED_OFF_H(n) ((4 * n) + 0x09)
41 
42 #define MAX_PWM_OUT 16
43 #define PWM_ONE_PERIOD_TICKS 4096
44 
45 int
46 pwm_pca9685_set_values(struct pwm_pca9685_data *data, uint32_t pwm, uint32_t on, uint32_t off)
47 {
48  uint8_t buf[5] = { 0 };
49 
50  if(!quarkX1000_i2c_is_available()) {
51  return -1;
52  }
53 
54  if(pwm > MAX_PWM_OUT) {
55  return -1;
56  }
57 
58  buf[0] = REG_LED_ON_L(pwm);
59 
60  if((on >= PWM_ONE_PERIOD_TICKS) || (off >= PWM_ONE_PERIOD_TICKS)) {
61  /* Treat as 100% */
62  buf[1] = 0x0;
63  buf[2] = (1 << 4);
64  buf[3] = 0x0;
65  buf[4] = 0x0;
66  } else if(off == 0) {
67  /* Treat it as 0% */
68  buf[1] = 0x0;
69  buf[2] = 0x0;
70  buf[3] = 0x0;
71  buf[4] = (1 << 4);
72  } else {
73  /* Populate registers accordingly */
74  buf[0] = (on & 0xFF);
75  buf[1] = ((on >> 8) & 0x0F);
76  buf[2] = (off & 0xFF);
77  buf[3] = ((off >> 8) & 0x0F);
78  }
79 
80  return quarkX1000_i2c_polling_write(buf, sizeof(buf), data->i2c_slave_addr);
81 }
82 int
83 pwm_pca9685_set_duty_cycle(struct pwm_pca9685_data *data, uint32_t pwm, uint8_t duty)
84 {
85  uint32_t on, off;
86 
87  if(duty == 0) {
88  on = 0;
89  off = 0;
90  } else if(duty >= 100) {
91  on = PWM_ONE_PERIOD_TICKS + 1;
92  off = PWM_ONE_PERIOD_TICKS + 1;
93  } else {
94  on = PWM_ONE_PERIOD_TICKS * duty / 100;
95  off = PWM_ONE_PERIOD_TICKS - 1;
96  }
97 
98  return pwm_pca9685_set_values(data, pwm, on, off);
99 }
100 int
101 pwm_pca9685_init(struct pwm_pca9685_data *data, uint16_t i2c_slave_addr)
102 {
103  uint8_t buf[2] = { 0 };
104 
105  /* has to init after I2C master */
106  if(!quarkX1000_i2c_is_available()) {
107  return -1;
108  }
109 
110  data->i2c_slave_addr = i2c_slave_addr;
111 
112  buf[0] = REG_MODE1;
113  buf[1] = (1 << 5);
114 
115  if(quarkX1000_i2c_polling_write(buf, 2, i2c_slave_addr) < 0) {
116  return -1;
117  }
118 
119  return 0;
120 }