libpruio  0.2
Input/Output driver for digital/analog lines on Beagleboard hardware
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
pruio_pwmss.bas
Go to the documentation of this file.
1 /'* \file pruio_pwmss.bas
2 \brief The PWMSS component source code.
3 
4 Source code file containing the function bodies of the PWMSS component.
5 The code for the subsystem PWMSS and its modules (eQEP, eCAP and ePWM)
6 is in here.
7 
8 '/
9 
10 '* The clock frequency
11 #define PWMSS_CLK 100e6
12 '* The half clock frequency
13 #define PWMSS_CLK_2 50e6
14 
15 
16 /'* \brief Constructor for the PWMSS subsystem configuration.
17 \param T A pointer of the calling PruIo structure.
18 
19 The constructor prepares the DRam parameters to run the pasm_init.p
20 instructions. The adresses of the subsystems and the adresses of the
21 clock registers get prepared, and the index of the last parameter gets
22 stored to compute the offset in the Init and Conf data blocks.
23 
24 \since 0.2
25 '/
26 CONSTRUCTOR PwmssUdt(BYVAL T AS Pruio_ PTR)
27  Top = T
28  WITH *Top
29  VAR i = .ParOffs
30  InitParA = i
31  i += 1 : .DRam[i] = &h48300000uL
32  i += 1 : .DRam[i] = IIF(.DevAct AND PRUIO_ACT_PWM0, &h44E000D4uL, 0)
33 
34  i += 1 : .DRam[i] = &h48302000uL
35  i += 1 : .DRam[i] = IIF(.DevAct AND PRUIO_ACT_PWM1, &h44E000CCuL, 0)
36 
37  i += 1 : .DRam[i] = &h48304000uL
38  i += 1 : .DRam[i] = IIF(.DevAct AND PRUIO_ACT_PWM2, &h44E000D8uL, 0)
39  .ParOffs = i
40  END WITH
41 END CONSTRUCTOR
42 
43 
44 /'* \brief Initialize the register context after running the pasm_init.p instructions (private).
45 \returns Zero on success (may return an error string in future versions).
46 
47 This is a private function, designed to be called from the main
48 constructor PruIo::PruIo(). It sets the pointers to the Init and Conf
49 structures in the data blocks. And it initializes some register
50 context, if the subsystem woke up and is enabled.
51 
52 \since 0.2
53 '/
54 FUNCTION PwmssUdt.initialize cdecl() AS zstring ptr
55  WITH *Top
56  var p_mem = .MOffs + .DRam[InitParA] _
57  , p_val = cast(any ptr, .DRam) + PRUIO_DAT_PWM
58  FOR i AS LONG = 0 TO PRUIO_AZ_PWMSS
59  Raw(i) = p_val
60  Raw(i)->CMax = 0
61  p_val += SIZEOF(PwmssArr)
62 
63  Init(i) = p_mem
64  Conf(i) = p_mem + .DSize
65 
66  WITH *Conf(i)
67  IF .ClAd = 0 ORELSE _
68  .IDVER = 0 THEN _ ' subsystem not enabled
69  .DeAd = 0 : .ClVa = 0 : p_mem += 16 : CONTINUE FOR
70  .ClVa = 2
71  .SYSCONFIG = 2 SHL 2
72  .CLKCONFIG = &b0100010001 ' enable all modules
73  ' ePWM registers
74  .TBPHS = 0
75  .TBSTS = &b110
76  ' eCAP registers
77  .TSCTR = 0 ' reset counter
78  .CTRPHS = 0
79  .ECCTL1 = &b111001100
80  .ECEINT = 0 ' disable all interupts
81  .ECCLR = &b11111110 ' clear all interupt flags
82  END WITH
83  p_mem += SIZEOF(PwmssSet)
84  NEXT
85  END WITH : RETURN 0
86 END FUNCTION
87 
88 
89 /'* \brief The constructor for PWM features of the PWMSS.
90 \param T A pointer of the calling PruIo structure.
91 
92 Each of the three Pulse Width Modulation SubSystem (PWMSS) in the CPU
93 contains modules (PWM, CAP and QEP). In order to create a clear API
94 from the user point of view, the functions to control the modules are
95 separated to extra classes. This UDT contains functions to control the
96 PWM module.
97 
98 The constructor just copies a pointer to the calling main UDT PruIo.
99 
100 \since 0.2
101 '/
102 CONSTRUCTOR PwmMod(BYVAL T AS Pruio_ PTR)
103  Top = T
104 END CONSTRUCTOR
105 
106 
107 /'* \brief Compute header pin PWM output configuration.
108 \param Ball The pin index.
109 \param Hz A pointer to output the frequency value (or 0 for no output).
110 \param Du A pointer to output the duty value (or 0 for no output).
111 \returns Zero on success, an error string otherwise.
112 
113 This functions computes the real PWM output of a header pin. The real
114 setting may differ from the parameters passed to function
115 PwmMod::setValue(). Use this function to compute the active settings.
116 
117 \since 0.2
118 '/
119 FUNCTION PwmMod.Value CDECL( _
120  BYVAL Ball AS UInt8 _
121  , BYVAL Hz AS Float_t PTR = 0 _
122  , BYVAL Du AS Float_t PTR = 0) AS ZSTRING PTR
123 
124  WITH *Top
125  BallCheck(" PWM", .Errr)
126 
127  VAR m = .BallInit[Ball] AND &b111
128  DIM AS ZSTRING PTR e
129  SELECT CASE AS CONST Ball
130  CASE P8_13 : e = IIF(m = 4, pwm_get(2, Hz, Du, 1), E1)
131  CASE P8_19 : e = IIF(m = 4, pwm_get(2, Hz, Du, 0), E1)
132  CASE P8_34 : e = IIF(m = 2, pwm_get(1, Hz, Du, 1), E1)
133  CASE P8_36 : e = IIF(m = 2, pwm_get(1, Hz, Du, 0), E1)
134  CASE P8_45 : e = IIF(m = 3, pwm_get(2, Hz, Du, 1), E1)
135  CASE P8_46 : e = IIF(m = 3, pwm_get(2, Hz, Du, 0), E1)
136  CASE P9_14 : e = IIF(m = 6, pwm_get(1, Hz, Du, 0), E1)
137  CASE P9_16 : e = IIF(m = 6, pwm_get(1, Hz, Du, 1), E1)
138  CASE P9_21 : e = IIF(m = 3, pwm_get(0, Hz, Du, 1), E1)
139  CASE P9_22 : e = IIF(m = 3, pwm_get(0, Hz, Du, 0), E1)
140  CASE P9_29 : e = IIF(m = 1, pwm_get(0, Hz, Du, 1), E1)
141  CASE P9_31 : e = IIF(m = 1, pwm_get(0, Hz, Du, 0), E1)
142  CASE P9_28 : e = IIF(m = 4, cap_get(2, Hz, Du), E1)
143  CASE P9_42 : e = IIF(m = 0, cap_get(0, Hz, Du), E1)
144  CASE ELSE : e = E0
145  END SELECT : IF e THEN .Errr = e : RETURN .Errr
146  END WITH : RETURN 0
147 END FUNCTION
148 
149 
150 /'* \brief Set PWM output on a header pin.
151 \param Ball The CPU ball number.
152 \param Hz The frequency to set (or -1 for no change).
153 \param Du The duty cycle to set (0.0 to 1.0, or -1 for no change).
154 \returns Zero on success (otherwise a string with an error message).
155 
156 This function sets PWM output on a header pin. PWM output may either be
157 generated by a eHRPWM or a eCAP module. Depending on the specified pin
158 number (parameter *Ball*), the corresponding PWMSS module gets
159 configured to the specified frequency and duty cycle.
160 
161 It's recommended to make the first call to this function before the
162 call to function PruIo::config(), because eCAP pins (P9_28 or
163 P9_42) can be used in PWM output and in CAP input mode. A mode change
164 happens when the configurations gets transfered from the host to the
165 PRU. Also, when an eCAP pin is currently used as input, the new PWM
166 output mode gets active after the next call to function
167 PruIo::config().
168 
169 Only positive values in the parameters *Hz* and *Du* force a
170 change. Pass a negative value to stay with the current setting. A duty
171 parameter greater than 1.0 gets limited to 1.0 (= 100%).
172 
173 In the first call to this function, the parameter *Hz* must be
174 greater than 0 (zero), to set a valid period time. But both outputs A
175 and B of an eHRPWM module must run at the same frequency, so you need
176 not set the *Hz* parameter when you set the *Du* for the secand
177 output.
178 
179 The hardware settings (integer values) are computed to match the passed
180 parameters *Hz* and *Du* as close as possible. Some parameter
181 combinations are impossible to match exactly, due to hardware
182 limitations (ie. 17 bit resolution in case of eHRPWM modules). Use
183 function PwmMod::Value() to compute the active settings and
184 calculate differences.
185 
186 '/
187 FUNCTION PwmMod.setValue CDECL( _
188  BYVAL Ball AS UInt8, _
189  BYVAL Hz AS Float_t, _
190  BYVAL Du AS Float_t) AS ZSTRING PTR
191 
192  WITH *Top
193  BallCheck(" PWM", .Errr)
194 
195  VAR m = .BallInit[Ball] AND &b111
196  DIM AS ZSTRING PTR e
197  SELECT CASE AS CONST Ball
198  CASE P8_13 : IF m <> 4 THEN IF .setPin(Ball, &h0C) THEN RETURN .Errr
199  RETURN pwm_set(2, Hz, -1., Du)
200  CASE P8_19 : IF m <> 4 THEN IF .setPin(Ball, &h0C) THEN RETURN .Errr
201  RETURN pwm_set(2, Hz, Du, -1.)
202  CASE P8_34 : IF m <> 2 THEN IF .setPin(Ball, &h0A) THEN RETURN .Errr
203  RETURN pwm_set(1, Hz, -1., Du)
204  CASE P8_36 : IF m <> 2 THEN IF .setPin(Ball, &h0A) THEN RETURN .Errr
205  RETURN pwm_set(1, Hz, Du, -1.)
206  CASE P8_45 : IF m <> 3 THEN IF .setPin(Ball, &h0B) THEN RETURN .Errr
207  RETURN pwm_set(2, Hz, -1., Du)
208  CASE P8_46 : IF m <> 3 THEN IF .setPin(Ball, &h0B) THEN RETURN .Errr
209  RETURN pwm_set(2, Hz, Du, -1.)
210  CASE P9_14 : IF m <> 6 THEN IF .setPin(Ball, &h0E) THEN RETURN .Errr
211  RETURN pwm_set(1, Hz, Du, -1.)
212  CASE P9_16 : IF m <> 6 THEN IF .setPin(Ball, &h0E) THEN RETURN .Errr
213  RETURN pwm_set(1, Hz, -1., Du)
214  CASE P9_21 : IF m <> 3 THEN IF .setPin(Ball, &h0B) THEN RETURN .Errr
215  RETURN pwm_set(0, Hz, -1., Du)
216  CASE P9_22 : IF m <> 3 THEN IF .setPin(Ball, &h0B) THEN RETURN .Errr
217  RETURN pwm_set(0, Hz, Du, -1.)
218  CASE P9_29 : IF m <> 1 THEN IF .setPin(Ball, &h09) THEN RETURN .Errr
219  RETURN pwm_set(0, Hz, -1., Du)
220  CASE P9_31 : IF m <> 1 THEN IF .setPin(Ball, &h09) THEN RETURN .Errr
221  RETURN pwm_set(0, Hz, Du, -1.)
222  CASE P9_28 : IF m <> 4 THEN IF .setPin(Ball, &h0C) THEN RETURN .Errr
223  RETURN cap_set(2, Hz, Du)
224  CASE P9_42 : IF m <> 0 THEN IF .setPin(Ball, &h08) THEN RETURN .Errr
225  RETURN cap_set(0, Hz, Du)
226  END SELECT : .Errr = E0 : RETURN .Errr
227  END WITH
228 END FUNCTION
229 
230 
231 /'* \brief Compute PWM output configuration from an eCAP module (private).
232 \param Nr The PWMSS subsystem index.
233 \param Freq A pointer to output the frequency value (or 0 for no output).
234 \param Duty A pointer to output the duty value (or 0 for no output).
235 \returns Zero on success, an error string otherwise.
236 
237 This functions computes the real PWM configuration of an eCAP module.
238 
239 \note This is a private function designed for internal use. It doesn't
240  check the validity of the *Nr* parameter. Values greater than
241  PRUIO_AZ_GPIO may result in wired behaviour.
242 
243 \since 0.2
244 '/
245 FUNCTION PwmMod.cap_get CDECL( _
246  BYVAL Nr AS UInt8 _
247  , BYVAL Freq AS Float_t PTR = 0 _
248  , BYVAL Duty AS Float_t PTR = 0) AS ZSTRING PTR
249 
250  WITH *Top->PwmSS->Conf(Nr)
251 /' PWM not enabled '/ : RETURN E2
252  IF 0 = BIT(.ECCTL2, 9) THEN RETURN @"eCAP module not in output mode"
253  IF Freq THEN *Freq = PWMSS_CLK / .CAP1
254  IF Duty THEN *Duty = .CAP2 / .CAP1
255  END WITH : RETURN 0
256 END FUNCTION
257 
258 
259 /'* \brief Configure PWM output at an eCAP module (private).
260 \param Nr The PWMSS subsystem index.
261 \param F The frequency to set (or -1 for no change).
262 \param D The duty cycle for output A (0.0 to 1.0, or -1 for no change).
263 \returns Zero on success, an error string otherwise.
264 
265 This functions configures an eCAP module for PWM output. It sets the
266 frequency and the duty cycle. Only positive values in these parameters
267 force a change. Pass a negative value to stay with the current setting.
268 A duty parameters greater than 1.0 gets limited to 1.0 (= 100%).
269 
270 \note This is a private function designed for internal use. It doesn't
271  check the validity of the *Nr* parameter. Values greater than
272  PRUIO_AZ_GPIO may result in wired behaviour.
273 
274 \since 0.2
275 '/
276 FUNCTION PwmMod.cap_set CDECL( _
277  BYVAL Nr AS UInt8 _
278  , BYVAL F AS Float_t _
279  , BYVAL D AS Float_t = 0.) AS ZSTRING PTR
280 
281  STATIC AS CONST Float_t _
282  f_min = PWMSS_CLK / &hFFFFFFFFuL '' minimal frequency
283  STATIC AS Float_t _
284  freq(...) = {0., 0., 0.} '' module frequencies
285  STATIC AS UInt32 _
286  cnt(...) = {0, 0, 0} _ '' module periods
287  , cmp(...) = {0, 0, 0} '' module compares
288 
289  WITH *Top
290  VAR r = 0
291  IF 2 <> .PwmSS->Conf(Nr)->ClVa THEN .Errr = E2 : RETURN E2 ' PWM not enabled
292  IF 0 = cnt(Nr) ANDALSO _
293  F <= 0. THEN .Errr = E3 : RETURN E3' set frequency first
294  WITH *.PwmSS
295  IF F > 0. THEN
296  IF F < f_min ORELSE _
297  F > PWMSS_CLK_2 THEN Top->Errr = E4 : RETURN E4 ' frequency not supported
298  cnt(Nr) = CUINT(PWMSS_CLK / F)
299  END IF
300  IF D >= 0 THEN cmp(Nr) = IIF(D > 1., cnt(Nr), CUINT(cnt(Nr) * D))
301 
302  .Conf(Nr)->CAP1 = cnt(Nr)
303  .Conf(Nr)->CAP2 = cmp(Nr)
304  IF .Conf(Nr)->ECCTL2 <> .PwmMode THEN
305  .Conf(Nr)->ECCTL2 = .PwmMode
306  .Raw(Nr)->CMax = 0
307  r = .PwmMode
308  END IF
309  END WITH
310 
311  IF .DRam[0] > PRUIO_MSG_IO_OK THEN RETURN 0
312 
313  WHILE .DRam[1] : WEND ' wait, if PRU is busy (should never happen)
314  .DRam[4] = cmp(Nr)
315  .DRam[3] = cnt(Nr)
316  .DRam[2] = .PwmSS->Conf(Nr)->DeAd + &h100
317  .DRam[1] = r OR (PRUIO_COM_PWM_CAP SHL 24)
318  END WITH : RETURN 0
319 END FUNCTION
320 
321 
322 /'* \brief Compute PWM output configuration from an eHRPWM module (private).
323 \param Nr The PWMSS subsystem index.
324 \param F A pointer to output the frequency value (or 0 for no output).
325 \param Du A pointer to output the duty value (or 0 for no output).
326 \param Mo The output channel (0 = A, otherwise B).
327 \returns Zero on success, an error string otherwise.
328 
329 This functions computes the real configuration of an eHRPWM module.
330 
331 \note This is a private function designed for internal use. It doesn't
332  check the validity of the *Nr* parameter. Values greater than
333  PRUIO_AZ_GPIO may result in wired behaviour.
334 
335 \since 0.2
336 '/
337 FUNCTION PwmMod.pwm_get CDECL( _
338  BYVAL Nr AS UInt8 _
339  , BYVAL F AS Float_t PTR = 0 _
340  , BYVAL Du AS Float_t PTR = 0 _
341  , BYVAL Mo AS UInt8) AS ZSTRING PTR
342 
343  WITH *Top->PwmSS->Conf(Nr)
344 /' PWM not enabled '/ : RETURN E2
345  VAR p = CAST(UInt32, .TBPRD)
346  IF F THEN
347  VAR d1 = (.TBCTL SHR 7) AND &b111, d2 = (.TBCTL SHR 10) AND &b111
348  VAR cg = p * IIF(d1, d1 SHL 1, 1) * (1 SHL d2)
349  *F = PWMSS_CLK / IIF(BIT(.TBCTL, 1), cg SHL 1, cg + 1)
350  END IF
351  IF Du THEN
352  VAR c = CAST(UInt32, IIF(Mo, .CMPB, .CMPA))
353  IF BIT(.TBCTL, 1) THEN ' count up-down
354  p SHL= 1
355  IF IIF(Mo, .AQCTLB, .AQCTLA) AND &b010001000000 THEN c = p - c
356  ELSE ' count up
357  p += 1
358  END IF
359  *Du = c / p
360  END IF
361  END WITH : RETURN 0
362 END FUNCTION
363 
364 
365 
366 /'* \brief Configure PWM output at a eHRPWM module (private).
367 \param Nr The PWMSS subsystem index.
368 \param F The frequency to set (or -1 for no change).
369 \param Da The duty cycle for output A (0.0 to 1.0, or -1 for no change).
370 \param Db The duty cycle for output B (0.0 to 1.0, or -1 for no change).
371 \returns Zero on success, an error string otherwise.
372 
373 This private function configures an eHRPWM module. It sets the common
374 frequency and both output duties A and B. Only positive values in these
375 parameters force a change. Pass a negative value to stay with the
376 current setting. Duty parameters (*Da* and *Db*) greater than 1.0 get
377 limited to 1.0 (= 100%).
378 
379 \note This is a private function designed for internal use. It doesn't
380  check the validity of the *Nr* parameter. Values greater than
381  PRUIO_AZ_GPIO may result in wired behaviour.
382 
383 \since 0.2
384 '/
385 FUNCTION PwmMod.pwm_set CDECL( _
386  BYVAL Nr AS UInt8 _
387  , BYVAL F AS Float_t _
388  , BYVAL Da AS Float_t = 0. _
389  , BYVAL Db AS Float_t = 0.) AS ZSTRING PTR
390 
391  STATIC AS CONST Float_t _
392  f_min = PWMSS_CLK_2 / &h6FFF900 '' minimal frequency (128 * 14 * 65535)
393  STATIC AS UInt16 pars(..., 1) = { _ '' dividers to scale the clock
394  { 1, 0} _
395  , { 2, 1} _
396  , { 4, 2} _
397  , { 6, 3} _
398  , { 8, 4} _
399  , { 10, 5} _
400  , { 12, 6} _
401  , { 14, 7} _
402  , { 16, 2 + 2 SHL 3} _
403  , { 20, 5 + 1 SHL 3} _
404  , { 24, 6 + 1 SHL 3} _
405  , { 28, 7 + 1 SHL 3} _
406  , { 32, 2 + 3 SHL 3} _
407  , { 40, 5 + 2 SHL 3} _
408  , { 48, 3 + 3 SHL 3} _
409  , { 56, 7 + 2 SHL 3} _
410  , { 64, 4 + 3 SHL 3} _
411  , { 80, 5 + 3 SHL 3} _
412  , { 96, 6 + 3 SHL 3} _
413  , { 112, 7 + 3 SHL 3} _
414  , { 128, 0 + 7 SHL 3} _
415  , { 160, 5 + 4 SHL 3} _
416  , { 192, 6 + 4 SHL 3} _
417  , { 224, 7 + 4 SHL 3} _
418  , { 256, 1 + 7 SHL 3} _
419  , { 320, 5 + 5 SHL 3} _
420  , { 384, 3 + 6 SHL 3} _
421  , { 448, 7 + 5 SHL 3} _
422  , { 512, 2 + 7 SHL 3} _
423  , { 640, 5 + 6 SHL 3} _
424  , { 768, 3 + 7 SHL 3} _
425  , { 896, 7 + 6 SHL 3} _
426  , {1024, 4 + 7 SHL 3} _
427  , {1280, 5 + 7 SHL 3} _
428  , {1536, 6 + 7 SHL 3} _
429  , {1792, 7 + 7 SHL 3} _
430  }
431 
432  STATIC AS Float_t _
433  freq(...) = {0., 0., 0.} _ ' module frequencies
434  , d_a(...) = {0., 0., 0.} _ ' module duty A
435  , d_b(...) = {0., 0., 0.} ' module duty B
436  STATIC AS UInt16 _
437  cnt(...) = {0, 0, 0} _ ' module periods
438  , c_a(...) = {0, 0, 0} _ ' module counters A
439  , c_b(...) = {0, 0, 0} ' module counters B
440 
441  VAR ctl = 0, aqc = 0
442  WITH *Top->PwmSS->Conf(Nr)
443  IF 2 <> .ClVa THEN Top->Errr = E2 : RETURN E2 ' PWM not enabled
444  IF 0 = cnt(Nr) THEN
445  IF F <= 0. THEN Top->Errr = E3 : RETURN E3 ' set frequency
446  ELSE
447  IF F > 0. ANDALSO freq(Nr) <> F THEN cnt(Nr) = 0
448  END IF
449 
450  IF 0 = cnt(Nr) THEN ' calc new period (frequency)
451  VAR cycle = IIF(F > f_min ANDALSO F <= PWMSS_CLK_2, CUINT(.5 + PWMSS_CLK / F), 0uL)
452  IF 2 > cycle THEN Top->Errr = E4 : RETURN E4 ' frequency not supported
453 
454  freq(Nr) = ABS(F)
455  IF cycle <= &h10000 THEN ' count up mode
456  cnt(Nr) = cycle - 1
457  ELSEIF cycle < &h20000 THEN ' no divisor count up-down mode
458  cnt(Nr) = cycle SHR 1
459  ctl = 2
460  ELSE ' divisor count up-down mode
461  VAR x = UBOUND(pars) SHR 2 _
462  , fac = cycle SHR 17 _
463  , i = x SHL 1
464 
465  WHILE x ' search matching clock divisor
466  i += IIF(fac >= pars(i, 0), x, -x)
467  x SHR= 1
468  WEND
469  WHILE fac >= pars(i, 0)
470  i += 1
471  WEND
472 
473  cnt(Nr) = CUINT(.5 + PWMSS_CLK_2 / pars(i, 0) / F)
474  ctl = 2 _
475  + (pars(i, 1) SHL 7) ' clock divisor
476  END IF
477  ctl += (1 SHL 15) _ ' free run
478  + (1 SHL 13) _ ' count up after SYNCI
479  + (3 SHL 4) ' dissable SYNCO
480  .TBCTL = ctl
481  .TBPRD = cnt(Nr)
482  .TBCNT = 0
483  c_a(Nr) = 0
484  c_b(Nr) = 0
485  END IF
486 
487  IF Da >= 0. THEN d_a(Nr) = IIF(Da > 1., 1., Da) : c_a(Nr) = 0
488  IF 0 = c_a(Nr) THEN ' calc new duty for A output
489  IF BIT(.TBCTL, 1) THEN ' up-down mode
490  IF d_a(Nr) >= .5 _
491  THEN .AQCTLA = &b000001000010 : c_a(Nr) = CUINT((cnt(Nr) SHL 1) * (1 - d_a(Nr))) _
492  ELSE .AQCTLA = &b000000010010 : c_a(Nr) = CUINT((cnt(Nr) SHL 1) * d_a(Nr))
493  ELSE ' up mode
494  .AQCTLA = &b000000010010 : c_a(Nr) = CUINT(.5 + (cnt(Nr) + 1) * d_a(Nr))
495  END IF
496  .CMPA = c_a(Nr)
497  END IF
498 
499  IF Db >= 0. THEN d_b(Nr) = IIF(Db > 1., 1., Db) : c_b(Nr) = 0
500  IF 0 = c_b(Nr) THEN ' calc new duty for B output
501  IF BIT(.TBCTL, 1) THEN ' up-down mode
502  IF d_b(Nr) >= .5 _
503  THEN .AQCTLB = &b010000000010 : c_b(Nr) = CUINT((cnt(Nr) SHL 1) * (1 - d_b(Nr))) _
504  ELSE .AQCTLB = &b000100000010 : c_b(Nr) = CUINT((cnt(Nr) SHL 1) * d_b(Nr))
505  ELSE ' up mode
506  .AQCTLB = &b000100000010 : c_b(Nr) = CUINT(.5 + (cnt(Nr) + 1) * d_b(Nr))
507  END IF
508  .CMPB = c_b(Nr)
509  END IF
510  aqc = .AQCTLA + .AQCTLB SHL 16
511  END WITH
512 
513  WITH *Top
514  IF .DRam[0] > PRUIO_MSG_IO_OK THEN RETURN 0
515 
516  WHILE .DRam[1] : WEND ' wait, if PRU is busy (should never happen)
517  .DRam[5] = cnt(Nr) SHL 16
518  .DRam[4] = aqc
519  .DRam[3] = c_a(Nr) + c_b(Nr) SHL 16
520  .DRam[2] = .PwmSS->Conf(Nr)->DeAd + &h200
521  .DRam[1] = IIF(ctl, ctl, 0) + PRUIO_COM_PWM SHL 24
522  END WITH : RETURN 0
523 END FUNCTION
524 
525 
526 /'* \brief The constructor for the CAP feature of the PWMSS.
527 \param T A pointer of the calling PruIo structure.
528 
529 Each of the three Pulse Width Modulation SubSystem (PWMSS) in the CPU
530 contains modules (PWM, CAP and QEP). In order to create a clear API
531 from the user point of view, the functions to control the modules are
532 separated to extra classes. This UDT contains functions to control the
533 CAP module, which is used to analyse the frequency and duty cycle of a
534 digital pulse train.
535 
536 The constructor just copies a pointer to the calling main UDT PruIo.
537 
538 \since 0.2
539 '/
540 CONSTRUCTOR CapMod(BYVAL T AS Pruio_ PTR)
541  Top = T
542 END CONSTRUCTOR
543 
544 
545 /'* \brief Configure a header pin as eCAP input.
546 \param Ball The CPU ball number to configure.
547 \param FLow Minimal frequency to measure in Hz (> .0232831).
548 \returns Zero on success (otherwise a string with an error message).
549 
550 This function configures a header pin for Capture and Analyse Pulse
551 (CAP) trains. The pins configuration gets checked. If it's not
552 configured as input for the CAP module in the PWMSS subsystem, libpruio
553 tries to adapt the pinmuxing. This fails if the program isn't executed
554 with admin privileges.
555 
556 Currently CAP is available on pins P9_42 and P9_28. The later is used
557 for MCASP-0 on the BBB.
558 
559 The parameter *FLow* specifies the minimal frequency to measure in Hz.
560 When not set (values < .0232831) and the input doesn't change, the
561 counter runs until an overflow before it returns the new values (in
562 this case frequency = 0 and duty cycle = 0). This lasts about 43
563 seconds before the new values are available. To shorten this time, you
564 can specify the lowest frequency. When a period without change is over,
565 the counter gets restarted.
566 
567 \since 0.2
568 '/
569 FUNCTION CapMod.config CDECL( _
570  BYVAL Ball AS UInt8 _
571  , BYVAL FLow AS Float_t = 0.) AS ZSTRING PTR
572 
573  static as UInt8 m
574  WITH *Top
575  BallCheck(" CAP", .Errr)
576 
577  m = .BallInit[Ball] AND &b111
578  dim AS ZSTRING PTR e
579  SELECT CASE AS CONST Ball
580  CASE P9_28 : IF m <> 4 THEN IF .setPin(Ball, &h24) THEN RETURN .Errr
581  m = 2
582  CASE P9_42 : IF m <> 0 THEN IF .setPin(Ball, &h20) THEN RETURN .Errr
583  'CASE 88 : IF m <> 2 THEN IF .setPin(Ball, &h22) THEN RETURN .Errr
584  'm = 1
585  'CASE 92 : IF m <> 4 THEN IF .setPin(Ball, &h24) THEN RETURN .Errr
586  'm = 2
587  'CASE 93 : IF m <> 4 THEN IF .setPin(Ball, &h24) THEN RETURN .Errr
588  'm = 1
589  'CASE 98 : IF m <> 3 THEN IF .setPin(Ball, &h23) THEN RETURN .Errr
590  'm = 2
591  'CASE 99 : IF m <> 3 THEN IF .setPin(Ball, &h23) THEN RETURN .Errr
592  'm = 1
593  CASE ELSE : .Errr = E0 : RETURN .Errr
594  END SELECT
595  END WITH
596  WITH *Top->PwmSS
597  IF 2 <> .Conf(m)->ClVa THEN Top->Errr = E2 : RETURN E2 ' CAP not enabled
598  VAR cnt = &hFFFFFFFFul
599  IF FLow > PWMSS_CLK/ &hFFFFFFFFul THEN
600  cnt = CUINT(PWMSS_CLK / FLow)
601  IF cnt < 200 THEN cnt = 200
602  END IF
603  .Raw(m)->CMax = cnt
604 
605  IF .Conf(m)->ECCTL2 <> .CapMode THEN ' eCAP module not in input mode
606  .Conf(m)->ECCTL2 = .CapMode
607  IF Top->DRam[0] > PRUIO_MSG_IO_OK THEN RETURN 0
608 
609  WHILE Top->DRam[1] : WEND ' wait, if PRU is busy
610  Top->DRam[2] = .Conf(m)->DeAd + &h100
611  Top->DRam[1] = .CapMode + (PRUIO_COM_CAP SHL 24)
612  END IF
613  END WITH : RETURN 0
614 END FUNCTION
615 
616 
617 /'* \brief Analyse a digital pulse train, get frequency and duty cycle.
618 \param Ball The CPU ball number to test.
619 \param Hz A pointer to store the frequency value (or null).
620 \param Du A pointer to store the duty cycle value (or null).
621 \returns Zero on success (otherwise a string with an error message).
622 
623 This function returns the frequency and duty cycle of a digital pulse
624 train on a header pin. The header pin needs to get configured first by
625 a call to function CapMod::config().
626 
627 The parameters *Hz* and *Du* contain the results of the last measured
628 period. You can pass a zero pointer to either of them if you don't need
629 this value.
630 
631 A period is limited by the counter resolution. The minimal frequency is
632 0.0232831 Hz, so a period lasts maximal 43 seconds. When the state of
633 the input pin doesn't change twice during a period, the counter
634 restarts and zero gets returned for both results (*Hz* and *Du*). The
635 minimal frequency can get adapted by parameter *FLow* in the previous
636 call to function CapMod::config().
637 
638 \since 0.2
639 '/
640 FUNCTION CapMod.Value CDECL( _
641  BYVAL Ball AS UInt8 _
642  , BYVAL Hz AS Float_t PTR = 0 _
643  , BYVAL Du AS Float_t PTR = 0) AS ZSTRING PTR
644 
645  static as UInt8 m
646  WITH *Top
647  BallCheck(" CAP", .Errr)
648 
649  m = .BallInit[Ball] AND &b111
650  dim AS ZSTRING PTR e
651  SELECT CASE AS CONST Ball
652  CASE P9_28 : IF m <> 4 THEN e = E1 ELSE m = 2
653  CASE P9_42 : IF m <> 0 THEN e = E1
654  'CASE 88 : IF m <> 2 THEN e = E1 ELSE m = 1
655  'CASE 92 : IF m <> 4 THEN e = E1 ELSE m = 2
656  'CASE 93 : IF m <> 4 THEN e = E1 ELSE m = 1
657  'CASE 98 : IF m <> 3 THEN e = E1 ELSE m = 2
658  'CASE 99 : IF m <> 3 THEN e = E1 ELSE m = 1
659  CASE ELSE : e = E0
660  END SELECT : IF e THEN .Errr = e : RETURN .Errr
661 
662  IF .DRam[0] > PRUIO_MSG_IO_OK THEN
663  IF Hz THEN *Hz = 0
664  IF Du THEN *Du = 0
665  .Errr = @"IO/RB mode not running" : RETURN .Errr
666  END IF
667  END WITH
668  WITH *Top->PwmSS->Raw(m)
669  IF .CMax THEN
670  IF Hz THEN *Hz = IIF(.C2, PWMSS_CLK / .C2, 0.)
671  IF Du THEN *Du = IIF(.C2, (.C2 - .C1) / .C2, 0.)
672  RETURN 0
673  end if
674  IF Hz THEN *Hz = 0
675  IF Du THEN *Du = 0
676 /' CAP not enabled '/ : RETURN E2
677  END WITH
678 END FUNCTION
679 
680 
681 
682 '/'* \brief The constructor for QEP features of the PWMSS.
683 '\param T A pointer of the calling PruIo structure.
684 
685 'FIXME
686 
687 '\since 0.2
688 ''/
689 'CONSTRUCTOR QepMod(BYVAL T AS Pruio_ PTR)
690  'Top = T
691 'END CONSTRUCTOR
692 
693 '/'* \brief Configure a header pin as eCAP input.
694 '\param BallA The CPU ball number for input A.
695 '\param BallB The CPU ball number for input B.
696 '\param BallI The CPU ball number for index input/output.
697 '\param BallS The CPU ball number for strobe input/output.
698 '\returns Zero on success (otherwise a string with an error message).
699 
700 'Each of the three The Pulse Width Modulation SubSystem (PWMSS) in the
701 'CPU contains modules (PWM, CAP and QEP). In order to create a clear
702 'API from the user point of view, the functions to control the modules
703 'are separated to extra classes. This UDT contains functions to control
704 'the QEP module.
705 
706 'The constructor just copies a pointer to the calling main UDT
707 'PruIo.
708 
709 
710 ''/
711 'FUNCTION QepMod.config CDECL( _
712  'BYVAL BallA AS UInt8 _
713  ', BYVAL BallB AS UInt8 = 0 _
714  ', BYVAL BallI AS UInt8 = 0 _
715  ', BYVAL BallS AS UInt8 = 0) AS ZSTRING PTR
716 
717  'WITH *Top
718  'var _
719  'Ball = BallA : BallCheck(" QEP", .Errr)
720  'Ball = BallB : BallCheck(" QEP", .Errr)
721  'Ball = BallI : BallCheck(" QEP", .Errr)
722  'Ball = BallS : BallCheck(" QEP", .Errr)
723 
724  'var m = .BallInit[BallA] and &b111
725  'static as zstring ptr e
726  'SELECT CASE AS CONST Ball
727  'CASE P8_12 : if m <> 3 then e = E1 else m = 2
728  'e = iif(BallB, iif(.BallInit[BallB] and &b111 <> 3, E1, e), e)
729  'e = iif(BallI, iif(.BallInit[BallI] and &b111 <> 3, E1, e), e)
730  'e = iif(BallS, iif(.BallInit[BallS] and &b111 <> 3, E1, e), e)
731  'CASE P8_41 : if m <> 4 then e = E1 else m = 2
732  'e = iif(BallB, iif(.BallInit[BallB] and &b111 <> 4, E1, e), e)
733  'e = iif(BallI, iif(.BallInit[BallI] and &b111 <> 4, E1, e), e)
734  'e = iif(BallS, iif(.BallInit[BallS] and &b111 <> 4, E1, e), e)
735  'CASE P8_35 : if m <> 6 then e = E1 else m = 1
736  'e = iif(BallB, iif(.BallInit[BallB] and &b111 <> 6, E1, e), e)
737  'e = iif(BallI, iif(.BallInit[BallI] and &b111 <> 6, E1, e), e)
738  'e = iif(BallS, iif(.BallInit[BallS] and &b111 <> 6, E1, e), e)
739  'CASE P9_42 : if m <> 1 then e = E1 else m = 0
740  'e = iif(BallB, iif(.BallInit[BallB] and &b111 <> 1, E1, e), e)
741  'e = iif(BallI, iif(.BallInit[BallI] and &b111 <> 1, E1, e), e)
742  'e = iif(BallS, iif(.BallInit[BallS] and &b111 <> 1, E1, e), e)
743  'CASE ELSE : e = E0
744  'END SELECT : if e then .Errr = e : RETURN .Errr
745 
746  'END WITH
747  'WITH *Top->PwmSS
748  'if 2 <> .Conf(m)->ClVa then _
749  'Top->Errr = E2 /' PWM not enabled '/ : RETURN E2
750 
751  'END WITH : return 0
752 'END FUNCTION
753 
754 
755 
756 '/'* \brief Analyse a digital pulse train, get frequency and duty cycle.
757 '\param Ball The CPU ball number to test.
758 '\param Posi A pointer to store the position value (or NULL).
759 '\returns Zero on success (otherwise a string with an error message).
760 
761 'FIXME
762 
763 '\since 0.2
764 ''/
765 'FUNCTION QepMod.Value CDECL( _
766  'BYVAL Ball AS UInt8 _
767  ', BYVAL Posi AS Float_t PTR = 0) AS ZSTRING PTR
768 
769  'WITH *Top
770  'BallCheck(" QEP", .Errr)
771 
772  'var m = .BallInit[Ball] and &b111
773  'static as zstring ptr e
774  'SELECT CASE AS CONST Ball
775  'CASE P8_12 : if m <> 3 then e = E1 else m = 2
776  'CASE P8_41 : if m <> 4 then e = E1 else m = 2
777  'CASE P8_35 : if m <> 6 then e = E1 else m = 1
778  'CASE P9_42 : if m <> 1 then e = E1 else m = 0
779  'CASE ELSE : e = E0
780  'END SELECT : if e then .Errr = e : RETURN .Errr
781 
782  'IF .DRam[0] > PRUIO_MSG_IO_OK THEN
783  'if Posi then *Posi = 0
784  '.Errr = @"IO mode not running" : return .Errr
785  'end if
786 
787  'END WITH
788  'WITH *Top->PwmSS->Raw(m)
789  ''if 0 = .CMax then Top->Errr = E2 /' QEP not enabled '/ : RETURN E2
790 
791  'END WITH : return 0
792 'END FUNCTION
793