Statistics
| Branch: | Revision:

ddr4s / fw / wiringPi / wiringPi / softPwm.c @ 32:cadb9025f1e0

History | View | Annotate | Download (4.45 KB)

1
/*
2
 * softPwm.c:
3
 *        Provide many channels of software driven PWM.
4
 *        Copyright (c) 2012-2017 Gordon Henderson
5
 ***********************************************************************
6
 * This file is part of wiringPi:
7
 *        https://projects.drogon.net/raspberry-pi/wiringpi/
8
 *
9
 *    wiringPi is free software: you can redistribute it and/or modify
10
 *    it under the terms of the GNU Lesser General Public License as
11
 *    published by the Free Software Foundation, either version 3 of the
12
 *    License, or (at your option) any later version.
13
 *
14
 *    wiringPi is distributed in the hope that it will be useful,
15
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *    GNU Lesser General Public License for more details.
18
 *
19
 *    You should have received a copy of the GNU Lesser General Public
20
 *    License along with wiringPi.
21
 *    If not, see <http://www.gnu.org/licenses/>.
22
 ***********************************************************************
23
 */
24

    
25
#include <stdio.h>
26
#include <malloc.h>
27
#include <pthread.h>
28

    
29
#include "wiringPi.h"
30
#include "softPwm.h"
31

    
32
// MAX_PINS:
33
//        This is more than the number of Pi pins because we can actually softPwm.
34
//        Once upon a time I let pins on gpio expanders be softPwm'd, but it's really
35
//        really not a good thing.
36

    
37
#define        MAX_PINS        64
38

    
39
// The PWM Frequency is derived from the "pulse time" below. Essentially,
40
//        the frequency is a function of the range and this pulse time.
41
//        The total period will be range * pulse time in µS, so a pulse time
42
//        of 100 and a range of 100 gives a period of 100 * 100 = 10,000 µS
43
//        which is a frequency of 100Hz.
44
//
45
//        It's possible to get a higher frequency by lowering the pulse time,
46
//        however CPU uage will skyrocket as wiringPi uses a hard-loop to time
47
//        periods under 100µS - this is because the Linux timer calls are just
48
//        not accurate at all, and have an overhead.
49
//
50
//        Another way to increase the frequency is to reduce the range - however
51
//        that reduces the overall output accuracy...
52

    
53
#define        PULSE_TIME        100
54

    
55
static volatile int marks         [MAX_PINS] ;
56
static volatile int range         [MAX_PINS] ;
57
static volatile pthread_t threads [MAX_PINS] ;
58
static volatile int newPin = -1 ;
59

    
60

    
61
/*
62
 * softPwmThread:
63
 *        Thread to do the actual PWM output
64
 *********************************************************************************
65
 */
66

    
67
static void *softPwmThread (void *arg)
68
{
69
  int pin, mark, space ;
70
  struct sched_param param ;
71

    
72
  param.sched_priority = sched_get_priority_max (SCHED_RR) ;
73
  pthread_setschedparam (pthread_self (), SCHED_RR, &param) ;
74

    
75
  pin = *((int *)arg) ;
76
  free (arg) ;
77

    
78
  pin    = newPin ;
79
  newPin = -1 ;
80

    
81
  piHiPri (90) ;
82

    
83
  for (;;)
84
  {
85
    mark  = marks [pin] ;
86
    space = range [pin] - mark ;
87

    
88
    if (mark != 0)
89
      digitalWrite (pin, HIGH) ;
90
    delayMicroseconds (mark * 100) ;
91

    
92
    if (space != 0)
93
      digitalWrite (pin, LOW) ;
94
    delayMicroseconds (space * 100) ;
95
  }
96

    
97
  return NULL ;
98
}
99

    
100

    
101
/*
102
 * softPwmWrite:
103
 *        Write a PWM value to the given pin
104
 *********************************************************************************
105
 */
106

    
107
void softPwmWrite (int pin, int value)
108
{
109
  if (pin < MAX_PINS)
110
  {
111
    /**/ if (value < 0)
112
      value = 0 ;
113
    else if (value > range [pin])
114
      value = range [pin] ;
115

    
116
    marks [pin] = value ;
117
  }
118
}
119

    
120

    
121
/*
122
 * softPwmCreate:
123
 *        Create a new softPWM thread.
124
 *********************************************************************************
125
 */
126

    
127
int softPwmCreate (int pin, int initialValue, int pwmRange)
128
{
129
  int res ;
130
  pthread_t myThread ;
131
  int *passPin ;
132

    
133
  if (pin >= MAX_PINS)
134
    return -1 ;
135

    
136
  if (range [pin] != 0)        // Already running on this pin
137
    return -1 ;
138

    
139
  if (pwmRange <= 0)
140
    return -1 ;
141

    
142
  passPin = malloc (sizeof (*passPin)) ;
143
  if (passPin == NULL)
144
    return -1 ;
145

    
146
  digitalWrite (pin, LOW) ;
147
  pinMode      (pin, OUTPUT) ;
148

    
149
  marks [pin] = initialValue ;
150
  range [pin] = pwmRange ;
151

    
152
  *passPin = pin ;
153
  newPin   = pin ;
154
  res      = pthread_create (&myThread, NULL, softPwmThread, (void *)passPin) ;
155

    
156
  while (newPin != -1)
157
    delay (1) ;
158

    
159
  threads [pin] = myThread ;
160

    
161
  return res ;
162
}
163

    
164

    
165
/*
166
 * softPwmStop:
167
 *        Stop an existing softPWM thread
168
 *********************************************************************************
169
 */
170

    
171
void softPwmStop (int pin)
172
{
173
  if (pin < MAX_PINS)
174
  {
175
    if (range [pin] != 0)
176
    {
177
      pthread_cancel (threads [pin]) ;
178
      pthread_join   (threads [pin], NULL) ;
179
      range [pin] = 0 ;
180
      digitalWrite (pin, LOW) ;
181
    }
182
  }
183
}