summaryrefslogtreecommitdiffstats
path: root/bsps/arm/lpc176x/pwm/pwmout.c
blob: c5ce3eb99852e23adde8a60277977cc4cdda1479 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
 * @file
 *
 * @ingroup lpc176x
 *
 * @brief PWM-Out controller for the mbed lpc1768 board.
 */

/*
 * Copyright (c) 2014 Taller Technologies.
 *
 * @author  Diaz Marcos (marcos.diaz@tallertechnologies.com)
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#include <rtems/status-checks.h>
#include <bsp/pwmout.h>
#include <bsp/pwmout-defs.h>

/**
 * @brief The low level device.
 */
static lpc176x_pwm_device *const pwm_device =
  (lpc176x_pwm_device *) PWM1_BASE_ADDR;

/**
 * @brief The possible output pins for each PWM output.
 */
static const lpc176x_pwm_pin pwm_pins[ PWM_OUTPUT_NUMBER ][ PWM_NUMBER_OF_PINS
] =
{
  { { 50u, LPC176X_PIN_FUNCTION_10 }, { 64u, LPC176X_PIN_FUNCTION_01 } },
  { { 52u, LPC176X_PIN_FUNCTION_10 }, { 65u, LPC176X_PIN_FUNCTION_01 } },
  { { 53u, LPC176X_PIN_FUNCTION_10 }, { 66u, LPC176X_PIN_FUNCTION_01 } },
  { { 55u, LPC176X_PIN_FUNCTION_10 }, { 67u, LPC176X_PIN_FUNCTION_01 } },
  { { 56u, LPC176X_PIN_FUNCTION_10 }, { 68u, LPC176X_PIN_FUNCTION_01 } },
  { { 58u, LPC176X_PIN_FUNCTION_10 }, { 69u, LPC176X_PIN_FUNCTION_01 } },
};

/**
 * @brief The pointers to the low level match registers for each PWM output.
 */
static volatile uint32_t *const pwm_match[ PWM_OUTPUT_NUMBER ] = {
  &PWM1MR1,
  &PWM1MR2,
  &PWM1MR3,
  &PWM1MR4,
  &PWM1MR5,
  &PWM1MR6
};

/**
 * @brief Checks if a pin number is valid for the given PWM,
 *  and sets the corresponding pin function for that pin.
 *
 * @param pin_number The pin number to search.
 * @param pwm In which PWM search for the pin number.
 * @param pin_function If the pin number is found, here we return
 *  the pin function for that pin number.
 * @return True if found, false otherwise.
 */
static inline bool is_found_in_this_pwm(
  const lpc176x_pin_number       pin_number,
  const lpc176x_pwm_number    pwm,
  lpc176x_pin_function *const pin_function
)
{
  lpc176x_pwm_pin_number pnumber = PWM_FIRST_PIN;
  bool                   found = false;

  while (!found && ( pnumber < PWM_NUMBER_OF_PINS ))
  {
    if ( pwm_pins[ pwm ][ pnumber ].pin_number == pin_number ) {
      found = true;
      *pin_function = pwm_pins[ pwm ][ pnumber ].pin_function;
    }/*else implies that the pin number was not found. Keep looking.*/
    ++pnumber;
  }
  return found;
}

/**
 * @brief Checks if a pin number is valid for any PWM,
 *  and sets the corresponding pin function for that pin.
 *
 * @param pin_number The pin number to search.
 * @param pwm If is found here we return in which PWM was found.
 * @param pin_function If the pin number is found the pin function
 *  for this pin number one will be returned.
 * @return True if found, false otherwise.
 */
static bool is_valid_pin_number(
  const lpc176x_pin_number       pin_number,
  lpc176x_pwm_number *const   pwm,
  lpc176x_pin_function *const pin_function
)
{
  bool               found = false;
  lpc176x_pwm_number pwm_local = PWMO_1;
  while(!found && ( pwm_local < PWM_OUTPUT_NUMBER ))
  {
    if ( is_found_in_this_pwm( pin_number, pwm_local, pin_function ) ) {
      *pwm = pwm_local;
      found = true;
    } /*else implies that the pin number was not found. Keep looking.*/
    ++pwm_local;
  }

  return found;
}

/**
 * @brief Sets the period for the given PWM.
 *
 * @param pwm The PWM output in which the period will be set.
 * @param period The period to set.
 */
static void set_period(
  const lpc176x_pwm_number   pwm,
  const lpc176x_microseconds period
)
{
  pwm_device->TCR = PWM_TCR_RESET;
  pwm_device->MR0 = period * PWM_PRESCALER_USECOND;
  pwm_device->LER |= PWM_LER_LATCH_MATCH_0;
  pwm_device->TCR = PWM_TCR_PWM | PWM_TCR_ENABLE;
}

/**
 * @brief Sets the pulsewidth for the given PWM.
 *
 * @param pwm The PWM output in which the pulsewidth will be set.
 * @param pwidth The pulse width to set.
 */
static void set_pulsewidth(
  const lpc176x_pwm_number pwm,
  lpc176x_microseconds     pwidth
)
{
  pwidth *= PWM_PRESCALER_USECOND;

  if ( pwm_device->MR0 == pwidth ) {
    ++pwidth;
  } /* Not the same as the period, do nothing.*/

  *( pwm_match[ pwm ] ) = pwidth;
  pwm_device->LER |= PWM_LER_LATCH( pwm );
}

rtems_status_code pwm_init( const lpc176x_pin_number pin_number )
{
  rtems_status_code    sc = RTEMS_INVALID_NUMBER;
  lpc176x_pin_function pin_function;
  lpc176x_pwm_number   pwm;

  if ( is_valid_pin_number( pin_number, &pwm, &pin_function ) ) {
    sc = lpc176x_module_enable( LPC176X_MODULE_PWM_1,
      LPC176X_MODULE_PCLK_DEFAULT );
    RTEMS_CHECK_SC( sc, "enable pwm module" );

    pwm_device->PR = 0;
    pwm_device->MCR = PWM_MCR_RESET_ON_MATCH0;
    pwm_device->PCR |= PWM_PCR_ENABLE_PWM( pwm );

    set_period( pwm, PWM_DEFAULT_PERIOD );
    set_pulsewidth( pwm, PWM_DEFAULT_PULSEWIDTH );

    lpc176x_pin_select( pin_number, pin_function );
  } /* else implies that the pin number is not valid.
     So, a RTEMS_INVALID_NUMBER will be returned.*/

  return sc;
}

rtems_status_code pwm_period(
  const lpc176x_pin_number    pin_number,
  const lpc176x_microseconds period
)
{
  rtems_status_code    sc = RTEMS_INVALID_NUMBER;
  lpc176x_pin_function pin_function;
  lpc176x_pwm_number   pwm;

  if ( is_valid_pin_number( pin_number, &pwm, &pin_function ) ) {
    sc = RTEMS_SUCCESSFUL;
    set_period( pwm, period );
  } /* else implies that the pin number is not valid.
     So, a RTEMS_INVALID_NUMBER will be returned.*/

  return sc;
}

rtems_status_code pwm_pulsewidth(
  const lpc176x_pin_number    pin_number,
  const lpc176x_microseconds pwidth
)
{
  rtems_status_code    sc = RTEMS_INVALID_NUMBER;
  lpc176x_pin_function pin_function;
  lpc176x_pwm_number   pwm;

  if ( is_valid_pin_number( pin_number, &pwm, &pin_function ) ) {
    sc = RTEMS_SUCCESSFUL;
    set_pulsewidth( pwm, pwidth );
  } /* Else wrong pin_number return RTEMS_INVALID_NUMBER*/

  return sc;
}