source: rtems/c/src/lib/libbsp/arm/beagle/pwm/pwm.c @ 6dc5c03f

5
Last change on this file since 6dc5c03f was 6dc5c03f, checked in by Punit Vara <punitvara@…>, on 07/04/16 at 18:02:05

Beaglebone: Add original BBBIO PWM driver

This patch adds the PWM driver code for the Beaglebone Black from BBBIO:

https://github.com/VegetableAvenger/BBBIOlib/blob/master/BBBio_lib/BBBiolib_PWMSS.c

This commit is for tracking purposes only; the next commit will adapt the code for
RTEMS.

  • Property mode set to 100644
File size: 12.0 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <sys/mman.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <errno.h>
7#include <time.h>
8#include "BBBiolib.h"
9/*-----------------------------------------------------------------------------------------------*/
10/*
11 * PWMSS Registers
12 *
13 * @Source : AM335x Technical Reference Manual ,page 1991
14 *           Table 15-5. PWMSS REGISTERS
15 *
16*/
17
18#define PWMSS0_MMAP_ADDR   0x48300000
19#define PWMSS1_MMAP_ADDR   0x48302000
20#define PWMSS2_MMAP_ADDR   0x48304000
21#define PWMSS_MMAP_LEN     0x1000
22
23#define PWMSS_IDVER    0x0
24#define PWMSS_SYSCONFIG    0x4
25#define PWMSS_CLKCONFIG    0x8
26#define PWMSS_CLKSTATUS    0xC
27
28/* EPWM Registers
29 *
30 * @Source : AM335x Technical Reference Manual ,page 2084
31 *           Table 15-58. EPWM REGISTERS
32 *
33*/
34#define EPWM_TBCTL 0x0
35#define EPWM_TBSTS 0x2
36#define EPWM_TBPHSHR   0x4
37#define EPWM_TBPHS 0x6
38#define EPWM_TBCNT 0x8
39#define EPWM_TBPRD 0xA
40#define EPWM_CMPCTL    0xE
41#define EPWM_CMPAHR    0x10
42#define EPWM_CMPA  0x12
43#define EPWM_CMPB  0x14
44#define EPWM_AQCTLA    0x16
45#define EPWM_AQCTLB    0x18
46#define EPWM_AQSFRC    0x1A
47#define EPWM_AQCSFRC   0x1C
48#define EPWM_DBCTL 0x1E
49#define EPWM_DBRED 0x20
50#define EPWM_DBFED 0x22
51/*-----------------------------------------------------------------------------------------------*/
52extern int memh;
53extern volatile unsigned int *CM_ptr;  /*c ontrol module */
54volatile unsigned int *cm_per_addr;
55
56
57const unsigned int PWMSS_AddressOffset[]={PWMSS0_MMAP_ADDR,
58                     PWMSS1_MMAP_ADDR,
59                     PWMSS2_MMAP_ADDR};
60volatile unsigned int *pwmss_ptr[3]     ={NULL, NULL, NULL} ;
61volatile unsigned int *epwm_ptr[3]      ={NULL, NULL, NULL} ;
62volatile unsigned int *ecap_ptr[3]      ={NULL, NULL, NULL} ;
63volatile unsigned int *eqep_ptr[3]      ={NULL, NULL, NULL} ;
64
65#define TBCTL_CTRMODE_UP        0x0
66#define TBCTL_CTRMODE_DOWN      0x1
67#define TBCTL_CTRMODE_UPDOWN    0x2
68#define TBCTL_CTRMODE_FREEZE    0x3
69/* ----------------------------------------------------------------------------------------------- */
70/* PWMSS Timebase clock check
71 *     check the timenase clock enable or not
72 *
73 * @param PWMSS_ID :  PWM sumsystem ID (BBBIO_PWMSS0 ,BBBIO_PWMSS1, BBBIO_PWMSS2)
74 *
75 * @return : 0 for disable timebase clock , 1 for enable for timebase clock
76 */
77static int PWMSS_TB_clock_check(unsigned int PWMSS_ID)
78{
79   volatile unsigned int* reg;
80   unsigned int reg_value ;
81
82   /* Control module check */
83   reg =(void *)CM_ptr + BBBIO_PWMSS_CTRL;
84   reg_value = *reg ;
85
86   return (reg_value & (1 << PWMSS_ID)) ;
87}
88
89/* ----------------------------------------------------------------------------------------------- */
90/* PWM subsystem system control
91 *     enable or disable module clock
92 *
93 * @param PWMSS_ID :  PWM sumsystem ID (BBBIO_PWMSS0 ,BBBIO_PWMSS1, BBBIO_PWMSS2).
94 * @param enable : 0 for disable , else for enable .
95 *
96 * @return : 1 for success ,  0 for error
97 */
98static int PWMSS_module_ctrl(unsigned int PWMSS_ID, int enable)
99{
100   volatile unsigned int *reg = NULL;
101   unsigned int module_set[] = {BBBIO_PWMSS0, BBBIO_PWMSS1, BBBIO_PWMSS2};
102   unsigned int module_clk_set[] = {BBBIO_CM_PER_EPWMSS0_CLKCTRL, BBBIO_CM_PER_EPWMSS1_CLKCTRL, BBBIO_CM_PER_EPWMSS2_CLKCTRL};
103   int ret = 1;
104
105   reg = (void*)cm_per_addr + module_clk_set[PWMSS_ID];
106   if(enable) {
107       if(PWMSS_TB_clock_check(module_set[PWMSS_ID])) {
108           /* Enable module clock */
109           *reg = 0x2; /* Module enable and fully functional */
110           return ret;
111       }
112#ifdef BBBIO_LIB_DBG
113       else {
114           printf("PWMSS_module_ctrl : PWMSS-%d timebase clock disable in Control Module\n", PWMSS_ID);
115       }
116#endif
117       ret = 0 ;
118   }
119   *reg = 0x3 << 16;   /* Module is disabled and cannot be accessed */
120   return ret;
121}
122
123/* ----------------------------------------------------------------------------------------------- */
124/* PWM init
125 * iolib_init will run this function automatically
126 *
127 *      @return         : 1 for success , 0 for failed
128 */
129
130int BBBIO_PWM_Init()
131{
132   int i = 0;
133
134   if (memh == 0) {
135#ifdef BBBIO_LIB_DBG
136       printf("BBBIO_PWM_Init: memory not mapped?\n");
137#endif
138       return 0;
139       }
140
141   /* Create Memory map */
142   for (i = 0 ; i < 3 ; i ++) {
143       pwmss_ptr[i] = mmap(0, PWMSS_MMAP_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, memh, PWMSS_AddressOffset[i]);
144       if(pwmss_ptr[i] == MAP_FAILED) {
145#ifdef BBBIO_LIB_DBG
146           printf("BBBIO_PWM_Init: PWMSS %d mmap failure!\n", i);
147#endif
148           goto INIT_ERROR ;
149       }
150       ecap_ptr[i] = (void *)pwmss_ptr[i] + 0x100 ;
151       eqep_ptr[i] = (void *)pwmss_ptr[i] + 0x180 ;
152       epwm_ptr[i] = (void *)pwmss_ptr[i] + 0x200 ;
153
154       if(!PWMSS_module_ctrl(i, 1)) {
155#ifdef BBBIO_LIB_DBG
156           printf("BBBIO_PWM_Init: PWMSS %d clock  failure!\n", i);
157#endif
158           goto INIT_ERROR ;
159       }
160       }
161   return 1;
162
163INIT_ERROR :
164   BBBIO_PWM_Release();
165   return 0;
166}
167
168/* ----------------------------------------------------------------------------------------------- */
169void BBBIO_PWM_Release()
170{
171   int i = 0;
172   for(i = 0 ; i < 3 ; i ++) {
173       if(pwmss_ptr[i] != NULL) {
174           munmap((void *)pwmss_ptr[i], PWMSS_MMAP_LEN);
175           pwmss_ptr[i] = NULL;
176           ecap_ptr[i] = NULL;
177           eqep_ptr[i] = NULL;
178           epwm_ptr[i] = NULL;
179       }
180   }
181}
182
183/* ----------------------------------------------------------------------------------------------- */
184/* PWMSS status (no effect now)
185 *     set pluse rgument of epwm module
186 *
187 *      @param PWMID    : EPWMSS number , 0~3
188 *
189 *      @return         : 1 for success , 0 for failed
190 */
191int BBBIO_PWMSS_Status(unsigned int PWMID)
192{
193   int param_error = 1;
194   volatile unsigned int* reg;
195   unsigned int reg_value ;
196
197   if (memh == 0)
198            param_error = 0;
199
200       if (PWMID > 2)      /* if input is not EPWMSS 0~ WPEMSS 2 */
201            param_error = 0;
202
203       if (param_error == 0) {
204#ifdef BBBIO_LIB_DBG
205       printf("BBBIO_PWM_Status: parameter error!\n");
206#endif
207       return 0;
208   }
209
210   reg =(void *)CM_ptr + BBBIO_PWMSS_CTRL;
211
212   reg_value = *reg >> PWMID & 0x01 ;
213   if(reg_value == 0) {
214       printf("PWMSS [%d] Timebase clock Disable , Control Module [pwmss_ctrl register]\n", PWMID);
215   }
216   else {
217       reg=(void *)pwmss_ptr[PWMID] + PWMSS_CLKSTATUS;
218       reg_value = *reg ;
219
220       printf("PWMSS [%d] :\tCLKSTOP_ACK %d , CLK_EN_ACK %d , CLKSTOP_ACK %d , CLK_EN_ACK %d , CLKSTOP_ACK %d , CLK_EN_ACK %d\n",
221           PWMID ,
222           reg_value >>9 & 0x1 ,
223           reg_value >>8 & 0x1 ,
224           reg_value >>5 & 0x1 ,
225           reg_value >>4 & 0x1 ,
226           reg_value >>1 & 0x1 ,
227           reg_value >>0 & 0x1 );
228   }
229   return 1 ;
230}
231/* ----------------------------------------------------------------------------------------------- */
232/* PWMSS setting
233 *     set pluse rgument of epwm module
234 *
235 *      @param PWMID    : EPWMSS number , 0~2
236 *      @param HZ      : pluse HZ
237 *      @param dutyA    : Duty Cycle in ePWM A
238 *      @param dutyB    : Duty Cycle in ePWM B
239 *
240 *      @return         : 1 for success , 0 for failed
241 *
242 *      @example        :  BBBIO_PWMSS_Setting(0 , 50.0f , 50.0f , 25.0f);     // Generate 50HZ pwm in PWM0 ,
243 *                                     // duty cycle is 50% for ePWM0A , 25% for ePWM0B
244 *
245 *     @Note :
246 *         find an number nearst 65535 for TBPRD , to improve duty precision,
247 *
248 *     Using big TBPRD can increase the range of CMPA and CMPB ,
249 *         and it means we can get better precision on duty cycle.
250 *
251 *     EX : 20.25% duty cycle
252 *                  on TBPRD = 62500 , CMPA = 12656.25 ( .25 rejection) , real duty : 20.2496% (12656 /62500)
253 *                  on TBPRD = 6250  , CMPA = 1265.625 ( .625 rejection), real duty : 20.24%   (1265 6250)
254 *                  on TBPRD = 500   , CMPA = 101.25   ( .25 rejection) , real duty : 20.2%    (101/500)
255 *
256 *     Divisor = CLKDIV * HSPCLKDIV
257 *                 1 TBPRD : 10 ns (default)
258 *         65535 TBPRD : 655350 ns
259 *         65535 TBPRD : 655350 * Divisor ns  = X TBPRD : Cyclens
260 *
261 *         accrooding to that , we must find a Divisor value , let X nearest 65535 .
262 *         so , Divisor must  Nearest Cyclens/655350
263*/
264
265int BBBIO_PWMSS_Setting(unsigned int PWMID , float HZ ,float dutyA ,float dutyB)
266{
267   int param_error = 1;
268   volatile unsigned short* reg16 ;
269        if (memh == 0)
270            param_error = 0;
271        if (PWMID > 2)              // if input is not EPWMSS 0~ WPEMSS 2
272            param_error = 0;
273   if (HZ < 0 )
274       param_error = 0;
275   if(dutyA < 0.0f || dutyA > 100.0f || dutyB < 0.0f || dutyB > 100.0f)
276       param_error = 0;
277
278        if (param_error == 0) {
279#ifdef BBBIO_LIB_DBG
280       printf("BBBIO_PWMSS_Setting: parameter error!\n");
281#endif
282       return 0;
283        }
284
285   dutyA /= 100.0f ;
286   dutyB /= 100.0f ;
287
288   /* compute neccessary TBPRD */
289   float Cyclens =0.0f ;
290   float Divisor =0;
291   int i , j ;
292   const float CLKDIV_div[] = {1.0 ,2.0 ,4.0 ,8.0 ,16.0 ,32.0 , 64.0 , 128.0};
293   const float HSPCLKDIV_div[] ={1.0 ,2.0 ,4.0 ,6.0 ,8.0 ,10.0 , 12.0 , 14.0};
294   int NearCLKDIV =7;
295   int NearHSPCLKDIV =7;
296   int NearTBPRD =0;
297
298   Cyclens = 1000000000.0f / HZ ; /* 10^9 / HZ , comput time per cycle (ns) */
299
300
301   Divisor =  (Cyclens / 655350.0f) ;  /* am335x provide (128*14) divider , and per TBPRD means 10 ns when divider /1 ,
302                        * and max TBPRD is 65535 , so , the max cycle is 128*14* 65535 *10ns
303                        */
304#ifdef BBBIO_LIB_DBG
305   printf("Cyclens %f , Divisor %f\n", Cyclens, Divisor);
306#endif
307
308   if(Divisor > (128 * 14)) {
309#ifdef BBBIO_LIB_DBG
310       printf("BBBIO_PWMSS_Setting : Can't generate %f HZ \n", HZ);
311#endif
312       return 0;
313   }
314   else {
315       /* using Exhaustive Attack metho */
316       for(i = 0 ; i < 8 ; i ++) {
317           for(j = 0 ; j < 8 ; j ++) {
318               if((CLKDIV_div[i] * HSPCLKDIV_div[j]) < (CLKDIV_div[NearCLKDIV] * HSPCLKDIV_div[NearHSPCLKDIV]) &&
319                 ((CLKDIV_div[i] * HSPCLKDIV_div[j]) > Divisor)) {
320                   NearCLKDIV = i ;
321                   NearHSPCLKDIV = j ;
322               }
323           }
324       }
325#ifdef BBBIO_LIB_DBG
326       printf("nearest CLKDIV %f , HSPCLKDIV %f\n" ,CLKDIV_div[NearCLKDIV] ,HSPCLKDIV_div[NearHSPCLKDIV]);
327#endif
328       NearTBPRD = (Cyclens / (10.0 *CLKDIV_div[NearCLKDIV] *HSPCLKDIV_div[NearHSPCLKDIV])) ;
329
330#ifdef BBBIO_LIB_DBG
331       printf("nearest TBPRD %d, %f %f\n ",NearTBPRD,NearTBPRD * dutyA, NearTBPRD * dutyB);
332#endif
333
334       /* setting clock diver and freeze time base */
335       reg16=(void*)epwm_ptr[PWMID] +EPWM_TBCTL;
336       *reg16 = TBCTL_CTRMODE_FREEZE | (NearCLKDIV << 10) | (NearHSPCLKDIV << 7);
337
338       /*  setting duty A and duty B */
339       reg16=(void*)epwm_ptr[PWMID] +EPWM_CMPB;
340       *reg16 =(unsigned short)((float)NearTBPRD * dutyB);
341
342       reg16=(void*)epwm_ptr[PWMID] +EPWM_CMPA;
343       *reg16 =(unsigned short)((float)NearTBPRD * dutyA);
344
345       reg16=(void*)epwm_ptr[PWMID] +EPWM_TBPRD;
346       *reg16 =(unsigned short)NearTBPRD;
347
348       /* reset time base counter */
349       reg16 = (void *)epwm_ptr[PWMID] + EPWM_TBCNT;
350       *reg16 = 0;
351   }
352   return 1;
353}
354/* ----------------------------------------------------------------------------------------------- */
355/* Enable/Disable ehrPWM module
356 *      @param PWMID    : PWMSS number , 0~2
357 *
358 *      @return         : void
359 *
360 *      @example        : BBBIO_PWMSS_Enable(0) ;// Enable PWMSS 0
361 */
362
363void BBBIO_ehrPWM_Enable(unsigned int PWMSS_ID)
364{
365   volatile unsigned short *reg16 ;
366
367   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLA;
368   *reg16 = 0x2 | ( 0x3 << 4) ;
369       
370   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLB;
371   *reg16 = 0x2 | ( 0x3 << 8) ;
372
373   reg16 = (void *)epwm_ptr[PWMSS_ID] + EPWM_TBCNT;
374   *reg16 = 0;
375
376        reg16=(void *)epwm_ptr[PWMSS_ID] + EPWM_TBCTL;
377   *reg16 &= ~0x3;
378}
379
380void BBBIO_ehrPWM_Disable(unsigned int PWMSS_ID)
381{
382   volatile unsigned short *reg16 ;
383        reg16=(void *)epwm_ptr[PWMSS_ID] + EPWM_TBCTL;
384        *reg16 |= 0x3;
385
386   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLA;
387   *reg16 = 0x1 | ( 0x3 << 4) ;
388       
389   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLB;
390   *reg16 = 0x1 | ( 0x3 << 8) ;
391
392   reg16 = (void *)epwm_ptr[PWMSS_ID] + EPWM_TBCNT;
393   *reg16 = 0;
394}
395//--------------------------------------------------------
396
397
398
399
400
401
402
403
404
405
406
407
Note: See TracBrowser for help on using the repository browser.