Home | History | Annotate | Download | only in pwm
      1 /*
      2  * Author: Thomas Ingleby <thomas.c.ingleby (at) intel.com>
      3  * Author: Brendan Le Foll <brendan.le.foll (at) intel.com>
      4  * Copyright (c) 2014, 2015 Intel Corporation.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining
      7  * a copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sublicense, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be
     15  * included in all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 #include <stdlib.h>
     27 #include <sys/stat.h>
     28 #include <unistd.h>
     29 #include <limits.h>
     30 
     31 #include "pwm.h"
     32 #include "mraa_internal.h"
     33 
     34 #define MAX_SIZE 64
     35 #define SYSFS_PWM "/sys/class/pwm"
     36 
     37 static int
     38 mraa_pwm_setup_duty_fp(mraa_pwm_context dev)
     39 {
     40     char bu[MAX_SIZE];
     41     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/duty_cycle", dev->chipid, dev->pin);
     42 
     43     dev->duty_fp = open(bu, O_RDWR);
     44     if (dev->duty_fp == -1) {
     45         return 1;
     46     }
     47     return 0;
     48 }
     49 
     50 static mraa_result_t
     51 mraa_pwm_write_period(mraa_pwm_context dev, int period)
     52 {
     53     if (IS_FUNC_DEFINED(dev, pwm_period_replace)) {
     54         mraa_result_t result = dev->advance_func->pwm_period_replace(dev, period);
     55         if (result == MRAA_SUCCESS) {
     56             dev->period = period;
     57         }
     58         return result;
     59     }
     60     char bu[MAX_SIZE];
     61     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/period", dev->chipid, dev->pin);
     62 
     63     int period_f = open(bu, O_RDWR);
     64     if (period_f == -1) {
     65         syslog(LOG_ERR, "pwm: Failed to open period for writing");
     66         return MRAA_ERROR_INVALID_RESOURCE;
     67     }
     68     char out[MAX_SIZE];
     69     int length = snprintf(out, MAX_SIZE, "%d", period);
     70     if (write(period_f, out, length * sizeof(char)) == -1) {
     71         close(period_f);
     72         return MRAA_ERROR_INVALID_RESOURCE;
     73     }
     74 
     75     close(period_f);
     76     dev->period = period;
     77     return MRAA_SUCCESS;
     78 }
     79 
     80 static mraa_result_t
     81 mraa_pwm_write_duty(mraa_pwm_context dev, int duty)
     82 {
     83     if (dev->duty_fp == -1) {
     84         if (mraa_pwm_setup_duty_fp(dev) == 1) {
     85             return MRAA_ERROR_INVALID_HANDLE;
     86         }
     87     }
     88     char bu[64];
     89     int length = sprintf(bu, "%d", duty);
     90     if (write(dev->duty_fp, bu, length * sizeof(char)) == -1)
     91         return MRAA_ERROR_INVALID_RESOURCE;
     92     return MRAA_SUCCESS;
     93 }
     94 
     95 static int
     96 mraa_pwm_read_period(mraa_pwm_context dev)
     97 {
     98     char bu[MAX_SIZE];
     99     char output[MAX_SIZE];
    100     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/period", dev->chipid, dev->pin);
    101 
    102     int period_f = open(bu, O_RDWR);
    103     if (period_f == -1) {
    104         syslog(LOG_ERR, "pwm: Failed to open period for reading");
    105         return 0;
    106     }
    107     off_t size = lseek(period_f, 0, SEEK_END);
    108     lseek(period_f, 0, SEEK_SET);
    109 
    110     ssize_t rb = read(period_f, output, size + 1);
    111     close(period_f);
    112 
    113     if (rb < 0) {
    114         syslog(LOG_ERR, "pwm: Error in reading period");
    115         return -1;
    116     }
    117 
    118     char* endptr;
    119     long int ret = strtol(output, &endptr, 10);
    120     if ('\0' != *endptr && '\n' != *endptr) {
    121         syslog(LOG_ERR, "pwm: Error in string conversion");
    122         return -1;
    123     } else if (ret > INT_MAX || ret < INT_MIN) {
    124         syslog(LOG_ERR, "pwm: Number is invalid");
    125         return -1;
    126     }
    127     dev->period = (int) ret;
    128     return (int) ret;
    129 }
    130 
    131 static int
    132 mraa_pwm_read_duty(mraa_pwm_context dev)
    133 {
    134     if (dev->duty_fp == -1) {
    135         if (mraa_pwm_setup_duty_fp(dev) == 1) {
    136             return MRAA_ERROR_INVALID_HANDLE;
    137         }
    138     } else {
    139         lseek(dev->duty_fp, 0, SEEK_SET);
    140     }
    141     off_t size = lseek(dev->duty_fp, 0, SEEK_END);
    142     lseek(dev->duty_fp, 0, SEEK_SET);
    143     char output[MAX_SIZE];
    144     ssize_t rb = read(dev->duty_fp, output, size + 1);
    145     if (rb < 0) {
    146         syslog(LOG_ERR, "pwm: Error in reading duty");
    147         return -1;
    148     }
    149 
    150     char* endptr;
    151     long int ret = strtol(output, &endptr, 10);
    152     if ('\0' != *endptr && '\n' != *endptr) {
    153         syslog(LOG_ERR, "pwm: Error in string converstion");
    154         return -1;
    155     } else if (ret > INT_MAX || ret < INT_MIN) {
    156         syslog(LOG_ERR, "pwm: Number is invalid");
    157         return -1;
    158     }
    159     return (int) ret;
    160 }
    161 
    162 static mraa_pwm_context
    163 mraa_pwm_init_internal(mraa_adv_func_t* func_table, int chipin, int pin)
    164 {
    165     mraa_pwm_context dev = (mraa_pwm_context) calloc(1,sizeof(struct _pwm));
    166     if (dev == NULL) {
    167         return NULL;
    168     }
    169     dev->duty_fp = -1;
    170     dev->chipid = chipin;
    171     dev->pin = pin;
    172     dev->period = -1;
    173     dev->advance_func = func_table;
    174 
    175     return dev;
    176 }
    177 
    178 mraa_pwm_context
    179 mraa_pwm_init(int pin)
    180 {
    181     if (plat == NULL) {
    182         syslog(LOG_ERR, "pwm: Platform Not Initialised");
    183         return NULL;
    184     }
    185     if (mraa_is_sub_platform_id(pin)) {
    186         syslog(LOG_NOTICE, "pwm: Using sub platform is not supported");
    187         return NULL;
    188     }
    189     if (plat->pins[pin].capabilites.pwm != 1) {
    190         syslog(LOG_ERR, "pwm: pin not capable of pwm");
    191         return NULL;
    192     }
    193 
    194     if (plat->adv_func->pwm_init_replace != NULL) {
    195         return plat->adv_func->pwm_init_replace(pin);
    196     }
    197     if (plat->adv_func->pwm_init_pre != NULL) {
    198         if (plat->adv_func->pwm_init_pre(pin) != MRAA_SUCCESS)
    199             return NULL;
    200     }
    201 
    202     if (plat->pins[pin].capabilites.gpio == 1) {
    203         // This deserves more investigation
    204         mraa_gpio_context mux_i;
    205         mux_i = mraa_gpio_init_raw(plat->pins[pin].gpio.pinmap);
    206         if (mux_i == NULL) {
    207             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
    208             return NULL;
    209         }
    210         if (mraa_gpio_dir(mux_i, MRAA_GPIO_OUT) != MRAA_SUCCESS) {
    211             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
    212             return NULL;
    213         }
    214         if (mraa_gpio_write(mux_i, 1) != MRAA_SUCCESS) {
    215             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
    216             return NULL;
    217         }
    218         if (mraa_gpio_close(mux_i) != MRAA_SUCCESS) {
    219             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
    220             return NULL;
    221         }
    222     }
    223 
    224     if (plat->pins[pin].pwm.mux_total > 0) {
    225         if (mraa_setup_mux_mapped(plat->pins[pin].pwm) != MRAA_SUCCESS) {
    226             syslog(LOG_ERR, "pwm: Failed to set-up multiplexer");
    227             return NULL;
    228         }
    229     }
    230 
    231     int chip = plat->pins[pin].pwm.parent_id;
    232     int pinn = plat->pins[pin].pwm.pinmap;
    233 
    234     if (plat->adv_func->pwm_init_post != NULL) {
    235         mraa_pwm_context pret = mraa_pwm_init_raw(chip, pinn);
    236         mraa_result_t ret = plat->adv_func->pwm_init_post(pret);
    237         if (ret != MRAA_SUCCESS) {
    238             free(pret);
    239             return NULL;
    240         }
    241         return pret;
    242     }
    243     return mraa_pwm_init_raw(chip, pinn);
    244 }
    245 
    246 mraa_pwm_context
    247 mraa_pwm_init_raw(int chipin, int pin)
    248 {
    249     mraa_pwm_context dev = mraa_pwm_init_internal(plat == NULL ? NULL : plat->adv_func , chipin, pin);
    250     if (dev == NULL)
    251         return NULL;
    252 
    253     char directory[MAX_SIZE];
    254     snprintf(directory, MAX_SIZE, SYSFS_PWM "/pwmchip%d/pwm%d", dev->chipid, dev->pin);
    255     struct stat dir;
    256     if (stat(directory, &dir) == 0 && S_ISDIR(dir.st_mode)) {
    257         syslog(LOG_NOTICE, "pwm: Pin already exported, continuing");
    258         dev->owner = 0; // Not Owner
    259     } else {
    260         char buffer[MAX_SIZE];
    261         snprintf(buffer, MAX_SIZE, "/sys/class/pwm/pwmchip%d/export", dev->chipid);
    262         int export_f = open(buffer, O_WRONLY);
    263         if (export_f == -1) {
    264             syslog(LOG_ERR, "pwm: Failed to open export for writing");
    265             free(dev);
    266             return NULL;
    267         }
    268 
    269         char out[MAX_SIZE];
    270         int size = snprintf(out, MAX_SIZE, "%d", dev->pin);
    271         if (write(export_f, out, size * sizeof(char)) == -1) {
    272             syslog(LOG_WARNING, "pwm: Failed to write to export! Potentially already enabled");
    273             close(export_f);
    274             free(dev);
    275             return NULL;
    276         }
    277         dev->owner = 1;
    278         mraa_pwm_period_us(dev, plat->pwm_default_period);
    279         close(export_f);
    280     }
    281     mraa_pwm_setup_duty_fp(dev);
    282     return dev;
    283 }
    284 
    285 mraa_result_t
    286 mraa_pwm_write(mraa_pwm_context dev, float percentage)
    287 {
    288     if (dev->period == -1) {
    289         if (mraa_pwm_read_period(dev) <= 0)
    290             return MRAA_ERROR_NO_DATA_AVAILABLE;
    291     }
    292 
    293     if (percentage > 1.0f) {
    294         syslog(LOG_WARNING, "pwm: number greater than 1 entered, defaulting to 100%%");
    295         return mraa_pwm_write_duty(dev, dev->period);
    296     }
    297     return mraa_pwm_write_duty(dev, percentage * dev->period);
    298 }
    299 
    300 float
    301 mraa_pwm_read(mraa_pwm_context dev)
    302 {
    303     int period = mraa_pwm_read_period(dev);
    304     if (period > 0) {
    305         return (mraa_pwm_read_duty(dev) / (float) period);
    306     }
    307     return 0.0f;
    308 }
    309 
    310 mraa_result_t
    311 mraa_pwm_period(mraa_pwm_context dev, float seconds)
    312 {
    313     return mraa_pwm_period_ms(dev, seconds * 1000);
    314 }
    315 
    316 mraa_result_t
    317 mraa_pwm_period_ms(mraa_pwm_context dev, int ms)
    318 {
    319     return mraa_pwm_period_us(dev, ms * 1000);
    320 }
    321 
    322 mraa_result_t
    323 mraa_pwm_period_us(mraa_pwm_context dev, int us)
    324 {
    325     if (us < plat->pwm_min_period || us > plat->pwm_max_period) {
    326         syslog(LOG_ERR, "pwm: period value outside platform range");
    327         return MRAA_ERROR_INVALID_PARAMETER;
    328     }
    329     return mraa_pwm_write_period(dev, us * 1000);
    330 }
    331 
    332 mraa_result_t
    333 mraa_pwm_pulsewidth(mraa_pwm_context dev, float seconds)
    334 {
    335     return mraa_pwm_pulsewidth_ms(dev, seconds * 1000);
    336 }
    337 
    338 mraa_result_t
    339 mraa_pwm_pulsewidth_ms(mraa_pwm_context dev, int ms)
    340 {
    341     return mraa_pwm_pulsewidth_us(dev, ms * 1000);
    342 }
    343 
    344 mraa_result_t
    345 mraa_pwm_pulsewidth_us(mraa_pwm_context dev, int us)
    346 {
    347     return mraa_pwm_write_duty(dev, us * 1000);
    348 }
    349 
    350 mraa_result_t
    351 mraa_pwm_enable(mraa_pwm_context dev, int enable)
    352 {
    353     int status;
    354     if (enable != 0) {
    355         status = 1;
    356     } else {
    357         status = enable;
    358     }
    359     char bu[MAX_SIZE];
    360     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/enable", dev->chipid, dev->pin);
    361 
    362     int enable_f = open(bu, O_RDWR);
    363 
    364     if (enable_f == -1) {
    365         syslog(LOG_ERR, "pwm: Failed to open enable for writing");
    366         return MRAA_ERROR_INVALID_RESOURCE;
    367     }
    368     char out[2];
    369     int size = snprintf(out, sizeof(out), "%d", enable);
    370     if (write(enable_f, out, size * sizeof(char)) == -1) {
    371         syslog(LOG_ERR, "pwm: Failed to write to enable");
    372         close(enable_f);
    373         return MRAA_ERROR_INVALID_RESOURCE;
    374     }
    375     close(enable_f);
    376     return MRAA_SUCCESS;
    377 }
    378 
    379 mraa_result_t
    380 mraa_pwm_unexport_force(mraa_pwm_context dev)
    381 {
    382     char filepath[MAX_SIZE];
    383     snprintf(filepath, MAX_SIZE, "/sys/class/pwm/pwmchip%d/unexport", dev->chipid);
    384 
    385     int unexport_f = open(filepath, O_WRONLY);
    386     if (unexport_f == -1) {
    387         syslog(LOG_ERR, "pwm: Failed to open unexport for writing");
    388         return MRAA_ERROR_INVALID_RESOURCE;
    389     }
    390 
    391     char out[MAX_SIZE];
    392     int size = snprintf(out, MAX_SIZE, "%d", dev->pin);
    393     if (write(unexport_f, out, size * sizeof(char)) == -1) {
    394         syslog(LOG_ERR, "pwm: Failed to write to unexport");
    395         close(unexport_f);
    396         return MRAA_ERROR_INVALID_RESOURCE;
    397     }
    398 
    399     close(unexport_f);
    400     return MRAA_SUCCESS;
    401 }
    402 
    403 mraa_result_t
    404 mraa_pwm_unexport(mraa_pwm_context dev)
    405 {
    406     mraa_pwm_enable(dev, 0);
    407     if (dev->owner) {
    408         return mraa_pwm_unexport_force(dev);
    409     }
    410     return MRAA_ERROR_INVALID_RESOURCE;
    411 }
    412 
    413 mraa_result_t
    414 mraa_pwm_close(mraa_pwm_context dev)
    415 {
    416     mraa_pwm_unexport(dev);
    417     free(dev);
    418     return MRAA_SUCCESS;
    419 }
    420 
    421 mraa_result_t
    422 mraa_pwm_owner(mraa_pwm_context dev, mraa_boolean_t owner_new)
    423 {
    424     if (dev == NULL)
    425         return MRAA_ERROR_INVALID_RESOURCE;
    426     dev->owner = owner_new;
    427     return MRAA_SUCCESS;
    428 }
    429 
    430 mraa_result_t
    431 mraa_pwm_config_ms(mraa_pwm_context dev, int ms, float ms_float)
    432 {
    433     int old_dutycycle, old_period, status;
    434     old_dutycycle = mraa_pwm_read_duty(dev);
    435     old_period = mraa_pwm_read_period(dev);
    436     status = mraa_pwm_period_us(dev, ms * 1000);
    437     if (status != MRAA_SUCCESS) {
    438         mraa_pwm_write_duty(dev, old_dutycycle);
    439         return status;
    440     }
    441     status = mraa_pwm_write_duty(dev, 0);
    442     if (status != MRAA_SUCCESS) {
    443         return status;
    444     }
    445     status = mraa_pwm_pulsewidth_us(dev, ms_float * 1000);
    446     if (status != MRAA_SUCCESS) {
    447         mraa_pwm_write_duty(dev, old_dutycycle);
    448         mraa_pwm_write_period(dev, old_period);
    449         return status;
    450     }
    451     return MRAA_SUCCESS;
    452 }
    453 
    454 mraa_result_t
    455 mraa_pwm_config_percent(mraa_pwm_context dev, int ms, float percentage)
    456 {
    457     int old_dutycycle, old_period, status;
    458     old_dutycycle = mraa_pwm_read_duty(dev);
    459     old_period = mraa_pwm_read_period(dev);
    460     status = mraa_pwm_period_us(dev, ms * 1000);
    461     if (status != MRAA_SUCCESS) {
    462         mraa_pwm_write_duty(dev, old_dutycycle);
    463         return status;
    464     }
    465     status = mraa_pwm_write_duty(dev, 0);
    466     if (status != MRAA_SUCCESS) {
    467         return status;
    468     }
    469     status = mraa_pwm_pulsewidth_us(dev, (ms * 1000) * percentage);
    470     if (status != MRAA_SUCCESS) {
    471         mraa_pwm_write_duty(dev, old_dutycycle);
    472         mraa_pwm_write_period(dev, old_period);
    473         return status;
    474     }
    475     return MRAA_SUCCESS;
    476 }
    477 
    478 int
    479 mraa_pwm_get_max_period()
    480 {
    481     if (plat == NULL) {
    482         return -1;
    483     }
    484     return plat->pwm_max_period;
    485 }
    486 
    487 int
    488 mraa_pwm_get_min_period()
    489 {
    490     if (plat == NULL) {
    491         return -1;
    492     }
    493     return plat->pwm_min_period;
    494 }
    495