Blame view

RIOT/cpu/kinetis_common/periph/pwm.c 4.07 KB
a752c7ab   elopes   add first test an...
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
  /*
   * Copyright (C) 2014-2016 Freie Universität Berlin
   * Copyright (C) 2014 PHYTEC Messtechnik GmbH
   * Copyright (C) 2015-2016 Eistec AB
   *
   * This file is subject to the terms and conditions of the GNU Lesser
   * General Public License v2.1. See the file LICENSE in the top level
   * directory for more details.
   */
  
  /**
   * @ingroup     cpu_kinetis_common
   * @ingroup     drivers_periph_pwm
   *
   * @{
   *
   * @file
   * @brief       Low-level PWM driver implementation
   *
   * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
   * @author      Johann Fischer <j.fischer@phytec.de>
   * @author      Jonas Remmert <j.remmert@phytec.de>
   * @author      Joakim Nohlgård <joakim.nohlgard@eistec.se>
   *
   * @}
   */
  
  #include "cpu.h"
  #include "assert.h"
  #include "periph/pwm.h"
  
  #define PRESCALER_MAX       (7U)
  
  static inline FTM_Type *ftm(pwm_t pwm)
  {
      return pwm_config[pwm].ftm;
  }
  
  static void poweron(pwm_t pwm)
  {
      int ftm = pwm_config[pwm].ftm_num;
  
  #ifdef SIM_SCGC6_FTM2_SHIFT
      BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 1;
  #else
      if (ftm < 2) {
          BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm) = 1;
      }
      else if (ftm == 2) {
          BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_FTM2_SHIFT) = 1;
      }
  #endif
  }
  
  uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res)
  {
      uint8_t pre = 0;
  
      if (pwm >= PWM_NUMOF || ((res * freq) > CLOCK_BUSCLOCK)) {
          return 0;
      }
  
      /* figure out the clock settings
       * the resulting frequency is calculated by
       *      ticks := BUS_CLK / 2 ^ pre
       *  where `ticks` is `freq * res`
       *  and `pre` must be between [0, 7].
       *
       *  The resulting prescaler yields a timer frequency, which is the closest
       *  possible frequency requested. */
      while ((CLOCK_BUSCLOCK >> pre) > (res * freq)) {
          ++pre;
      }
      /* make sure the calculated prescaler is valid, else return */
      if (pre > PRESCALER_MAX) {
          return 0;
      }
  
      /* configure the used timer */
      poweron(pwm);
      /* disable write protect for changing settings */
      ftm(pwm)->MODE = FTM_MODE_WPDIS_MASK;
      /* clear any existing configuration */
      ftm(pwm)->COMBINE = 0;
      ftm(pwm)->CNTIN = 0;
      ftm(pwm)->SWOCTRL = 0;
      /* apply prescaler and set resolution */
      ftm(pwm)->SC = FTM_SC_PS(pre);
      ftm(pwm)->MOD = (res - 1);
  
      /* set CPWMS bit in the SC register in case of center aligned mode */
      if (mode == PWM_CENTER) {
          BITBAND_REG32(ftm(pwm)->SC, FTM_SC_CPWMS_SHIFT) = 1;
      }
  
      /* setup the configured channels */
      for (int i = 0; i < (int)pwm_config[pwm].chan_numof; i++) {
          /* configure the used pin */
          gpio_init_port(pwm_config[pwm].chan[i].pin,
                         PORT_PCR_MUX(pwm_config[pwm].chan[i].af));
          /* set the given mode */
          ftm(pwm)->CONTROLS[pwm_config[pwm].chan[i].ftm_chan].CnSC = mode;
          /* and reset the PWM to 0% duty cycle */
          ftm(pwm)->CONTROLS[pwm_config[pwm].chan[i].ftm_chan].CnV = 0;
      }
  
      /* and now we start the actual waveform generation */
      ftm(pwm)->SC |= FTM_SC_CLKS(1);
  
      /* finally we need to return the actual applied PWM frequency */
      return (CLOCK_BUSCLOCK >> pre) / res;
  }
  
  uint8_t pwm_channels(pwm_t pwm)
  {
      assert(pwm < PWM_NUMOF);
      return pwm_config[pwm].chan_numof;
  }
  
  void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value)
  {
      assert((pwm < PWM_NUMOF) && (channel < pwm_config[pwm].chan_numof));
      ftm(pwm)->CONTROLS[pwm_config[pwm].chan[channel].ftm_chan].CnV = value;
  }
  
  void pwm_poweron(pwm_t pwm)
  {
      assert(pwm < PWM_NUMOF);
      poweron(pwm);
      ftm(pwm)->SC |= FTM_SC_CLKS(1);
  }
  
  void pwm_poweroff(pwm_t pwm)
  {
      assert(pwm < PWM_NUMOF);
      int ftm_num = pwm_config[pwm].ftm_num;
  
      /* disable PWM generation */
      ftm(pwm)->SC &= ~(FTM_SC_CLKS_MASK);
  
      /* and power of the peripheral */
  #ifdef SIM_SCGC6_FTM2_SHIFT
      BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm_num) = 0;
  #else
      if (ftm_num < 2) {
          BITBAND_REG32(SIM->SCGC6, SIM_SCGC6_FTM0_SHIFT + ftm_num) = 0;
      }
      else if (ftm_num == 2) {
          BITBAND_REG32(SIM->SCGC3, SIM_SCGC3_FTM2_SHIFT) = 0;
      }
  #endif
  }