summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/beagle/pwm/pwm.c
blob: 7586a82381a67996606ccede03b8443c66286a7e (plain)
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include "BBBiolib.h"
/*-----------------------------------------------------------------------------------------------*/
/*
 * PWMSS Registers
 *
 * @Source : AM335x Technical Reference Manual ,page 1991
 *           Table 15-5. PWMSS REGISTERS
 *
*/

#define PWMSS0_MMAP_ADDR   0x48300000
#define PWMSS1_MMAP_ADDR   0x48302000
#define PWMSS2_MMAP_ADDR   0x48304000
#define PWMSS_MMAP_LEN     0x1000

#define PWMSS_IDVER    0x0
#define PWMSS_SYSCONFIG    0x4
#define PWMSS_CLKCONFIG    0x8
#define PWMSS_CLKSTATUS    0xC

/* EPWM Registers
 *
 * @Source : AM335x Technical Reference Manual ,page 2084
 *           Table 15-58. EPWM REGISTERS
 *
*/
#define EPWM_TBCTL 0x0
#define EPWM_TBSTS 0x2
#define EPWM_TBPHSHR   0x4
#define EPWM_TBPHS 0x6
#define EPWM_TBCNT 0x8
#define EPWM_TBPRD 0xA
#define EPWM_CMPCTL    0xE
#define EPWM_CMPAHR    0x10
#define EPWM_CMPA  0x12
#define EPWM_CMPB  0x14
#define EPWM_AQCTLA    0x16
#define EPWM_AQCTLB    0x18
#define EPWM_AQSFRC    0x1A
#define EPWM_AQCSFRC   0x1C
#define EPWM_DBCTL 0x1E
#define EPWM_DBRED 0x20
#define EPWM_DBFED 0x22
/*-----------------------------------------------------------------------------------------------*/
extern int memh;
extern volatile unsigned int *CM_ptr;  /*c ontrol module */
volatile unsigned int *cm_per_addr;


const unsigned int PWMSS_AddressOffset[]={PWMSS0_MMAP_ADDR,
                     PWMSS1_MMAP_ADDR,
                     PWMSS2_MMAP_ADDR};
volatile unsigned int *pwmss_ptr[3]     ={NULL, NULL, NULL} ;
volatile unsigned int *epwm_ptr[3]      ={NULL, NULL, NULL} ;
volatile unsigned int *ecap_ptr[3]      ={NULL, NULL, NULL} ;
volatile unsigned int *eqep_ptr[3]      ={NULL, NULL, NULL} ;

#define TBCTL_CTRMODE_UP        0x0
#define TBCTL_CTRMODE_DOWN      0x1
#define TBCTL_CTRMODE_UPDOWN    0x2
#define TBCTL_CTRMODE_FREEZE    0x3
/* ----------------------------------------------------------------------------------------------- */
/* PWMSS Timebase clock check
 *     check the timenase clock enable or not
 *
 * @param PWMSS_ID :  PWM sumsystem ID (BBBIO_PWMSS0 ,BBBIO_PWMSS1, BBBIO_PWMSS2)
 *
 * @return : 0 for disable timebase clock , 1 for enable for timebase clock
 */
static int PWMSS_TB_clock_check(unsigned int PWMSS_ID)
{
   volatile unsigned int* reg;
   unsigned int reg_value ;

   /* Control module check */
   reg =(void *)CM_ptr + BBBIO_PWMSS_CTRL;
   reg_value = *reg ;

   return (reg_value & (1 << PWMSS_ID)) ;
}

/* ----------------------------------------------------------------------------------------------- */
/* PWM subsystem system control
 *     enable or disable module clock
 *
 * @param PWMSS_ID :  PWM sumsystem ID (BBBIO_PWMSS0 ,BBBIO_PWMSS1, BBBIO_PWMSS2).
 * @param enable : 0 for disable , else for enable .
 *
 * @return : 1 for success ,  0 for error
 */
static int PWMSS_module_ctrl(unsigned int PWMSS_ID, int enable)
{
   volatile unsigned int *reg = NULL;
   unsigned int module_set[] = {BBBIO_PWMSS0, BBBIO_PWMSS1, BBBIO_PWMSS2};
   unsigned int module_clk_set[] = {BBBIO_CM_PER_EPWMSS0_CLKCTRL, BBBIO_CM_PER_EPWMSS1_CLKCTRL, BBBIO_CM_PER_EPWMSS2_CLKCTRL};
   int ret = 1;

   reg = (void*)cm_per_addr + module_clk_set[PWMSS_ID];
   if(enable) {
       if(PWMSS_TB_clock_check(module_set[PWMSS_ID])) {
           /* Enable module clock */
           *reg = 0x2; /* Module enable and fully functional */
           return ret;
       }
#ifdef BBBIO_LIB_DBG
       else {
           printf("PWMSS_module_ctrl : PWMSS-%d timebase clock disable in Control Module\n", PWMSS_ID);
       }
#endif
       ret = 0 ;
   }
   *reg = 0x3 << 16;   /* Module is disabled and cannot be accessed */
   return ret;
}

/* ----------------------------------------------------------------------------------------------- */
/* PWM init
 * iolib_init will run this function automatically
 *
 *      @return         : 1 for success , 0 for failed
 */

int BBBIO_PWM_Init()
{
   int i = 0;

   if (memh == 0) {
#ifdef BBBIO_LIB_DBG
       printf("BBBIO_PWM_Init: memory not mapped?\n");
#endif
       return 0;
       }

   /* Create Memory map */
   for (i = 0 ; i < 3 ; i ++) {
       pwmss_ptr[i] = mmap(0, PWMSS_MMAP_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, memh, PWMSS_AddressOffset[i]);
       if(pwmss_ptr[i] == MAP_FAILED) {
#ifdef BBBIO_LIB_DBG
           printf("BBBIO_PWM_Init: PWMSS %d mmap failure!\n", i);
#endif
           goto INIT_ERROR ;
       }
       ecap_ptr[i] = (void *)pwmss_ptr[i] + 0x100 ;
       eqep_ptr[i] = (void *)pwmss_ptr[i] + 0x180 ;
       epwm_ptr[i] = (void *)pwmss_ptr[i] + 0x200 ;

       if(!PWMSS_module_ctrl(i, 1)) {
#ifdef BBBIO_LIB_DBG
           printf("BBBIO_PWM_Init: PWMSS %d clock  failure!\n", i);
#endif
           goto INIT_ERROR ;
       }
       }
   return 1;

INIT_ERROR :
   BBBIO_PWM_Release();
   return 0;
}

/* ----------------------------------------------------------------------------------------------- */
void BBBIO_PWM_Release()
{
   int i = 0;
   for(i = 0 ; i < 3 ; i ++) {
       if(pwmss_ptr[i] != NULL) {
           munmap((void *)pwmss_ptr[i], PWMSS_MMAP_LEN);
           pwmss_ptr[i] = NULL;
           ecap_ptr[i] = NULL;
           eqep_ptr[i] = NULL;
           epwm_ptr[i] = NULL;
       }
   }
}

/* ----------------------------------------------------------------------------------------------- */
/* PWMSS status (no effect now)
 *     set pluse rgument of epwm module
 *
 *      @param PWMID    : EPWMSS number , 0~3
 *
 *      @return         : 1 for success , 0 for failed
 */
int BBBIO_PWMSS_Status(unsigned int PWMID)
{
   int param_error = 1;
   volatile unsigned int* reg;
   unsigned int reg_value ;

   if (memh == 0)
            param_error = 0;

       if (PWMID > 2)      /* if input is not EPWMSS 0~ WPEMSS 2 */
            param_error = 0;

       if (param_error == 0) {
#ifdef BBBIO_LIB_DBG
       printf("BBBIO_PWM_Status: parameter error!\n");
#endif
       return 0;
   }

   reg =(void *)CM_ptr + BBBIO_PWMSS_CTRL;

   reg_value = *reg >> PWMID & 0x01 ;
   if(reg_value == 0) {
       printf("PWMSS [%d] Timebase clock Disable , Control Module [pwmss_ctrl register]\n", PWMID);
   }
   else {
       reg=(void *)pwmss_ptr[PWMID] + PWMSS_CLKSTATUS;
       reg_value = *reg ;

       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",
           PWMID ,
           reg_value >>9 & 0x1 ,
           reg_value >>8 & 0x1 ,
           reg_value >>5 & 0x1 ,
           reg_value >>4 & 0x1 ,
           reg_value >>1 & 0x1 ,
           reg_value >>0 & 0x1 );
   }
   return 1 ;
}
/* ----------------------------------------------------------------------------------------------- */
/* PWMSS setting
 *     set pluse rgument of epwm module
 *
 *      @param PWMID    : EPWMSS number , 0~2
 *      @param HZ      : pluse HZ
 *      @param dutyA    : Duty Cycle in ePWM A
 *      @param dutyB    : Duty Cycle in ePWM B
 *
 *      @return         : 1 for success , 0 for failed
 *
 *      @example        :  BBBIO_PWMSS_Setting(0 , 50.0f , 50.0f , 25.0f);     // Generate 50HZ pwm in PWM0 ,
 *                                     // duty cycle is 50% for ePWM0A , 25% for ePWM0B
 *
 *     @Note :
 *         find an number nearst 65535 for TBPRD , to improve duty precision,
 *
 *     Using big TBPRD can increase the range of CMPA and CMPB ,
 *         and it means we can get better precision on duty cycle.
 *
 *     EX : 20.25% duty cycle
 *                  on TBPRD = 62500 , CMPA = 12656.25 ( .25 rejection) , real duty : 20.2496% (12656 /62500)
 *                  on TBPRD = 6250  , CMPA = 1265.625 ( .625 rejection), real duty : 20.24%   (1265 6250)
 *                  on TBPRD = 500   , CMPA = 101.25   ( .25 rejection) , real duty : 20.2%    (101/500)
 *
 *     Divisor = CLKDIV * HSPCLKDIV
 *                 1 TBPRD : 10 ns (default)
 *         65535 TBPRD : 655350 ns
 *         65535 TBPRD : 655350 * Divisor ns  = X TBPRD : Cyclens
 *
 *         accrooding to that , we must find a Divisor value , let X nearest 65535 .
 *         so , Divisor must  Nearest Cyclens/655350
*/

int BBBIO_PWMSS_Setting(unsigned int PWMID , float HZ ,float dutyA ,float dutyB)
{
   int param_error = 1;
   volatile unsigned short* reg16 ;
        if (memh == 0)
            param_error = 0;
        if (PWMID > 2)              // if input is not EPWMSS 0~ WPEMSS 2
            param_error = 0;
   if (HZ < 0 )
       param_error = 0;
   if(dutyA < 0.0f || dutyA > 100.0f || dutyB < 0.0f || dutyB > 100.0f)
       param_error = 0;

        if (param_error == 0) {
#ifdef BBBIO_LIB_DBG
       printf("BBBIO_PWMSS_Setting: parameter error!\n");
#endif
       return 0;
        }

   dutyA /= 100.0f ;
   dutyB /= 100.0f ;

   /* compute neccessary TBPRD */
   float Cyclens =0.0f ;
   float Divisor =0;
   int i , j ;
   const float CLKDIV_div[] = {1.0 ,2.0 ,4.0 ,8.0 ,16.0 ,32.0 , 64.0 , 128.0};
   const float HSPCLKDIV_div[] ={1.0 ,2.0 ,4.0 ,6.0 ,8.0 ,10.0 , 12.0 , 14.0};
   int NearCLKDIV =7;
   int NearHSPCLKDIV =7;
   int NearTBPRD =0;

   Cyclens = 1000000000.0f / HZ ; /* 10^9 / HZ , comput time per cycle (ns) */


   Divisor =  (Cyclens / 655350.0f) ;  /* am335x provide (128*14) divider , and per TBPRD means 10 ns when divider /1 ,
                        * and max TBPRD is 65535 , so , the max cycle is 128*14* 65535 *10ns
                        */
#ifdef BBBIO_LIB_DBG
   printf("Cyclens %f , Divisor %f\n", Cyclens, Divisor);
#endif

   if(Divisor > (128 * 14)) {
#ifdef BBBIO_LIB_DBG
       printf("BBBIO_PWMSS_Setting : Can't generate %f HZ \n", HZ);
#endif
       return 0;
   }
   else {
       /* using Exhaustive Attack metho */
       for(i = 0 ; i < 8 ; i ++) {
           for(j = 0 ; j < 8 ; j ++) {
               if((CLKDIV_div[i] * HSPCLKDIV_div[j]) < (CLKDIV_div[NearCLKDIV] * HSPCLKDIV_div[NearHSPCLKDIV]) &&
                 ((CLKDIV_div[i] * HSPCLKDIV_div[j]) > Divisor)) {
                   NearCLKDIV = i ;
                   NearHSPCLKDIV = j ;
               }
           }
       }
#ifdef BBBIO_LIB_DBG
       printf("nearest CLKDIV %f , HSPCLKDIV %f\n" ,CLKDIV_div[NearCLKDIV] ,HSPCLKDIV_div[NearHSPCLKDIV]);
#endif
       NearTBPRD = (Cyclens / (10.0 *CLKDIV_div[NearCLKDIV] *HSPCLKDIV_div[NearHSPCLKDIV])) ;

#ifdef BBBIO_LIB_DBG
       printf("nearest TBPRD %d, %f %f\n ",NearTBPRD,NearTBPRD * dutyA, NearTBPRD * dutyB);
#endif

       /* setting clock diver and freeze time base */
       reg16=(void*)epwm_ptr[PWMID] +EPWM_TBCTL;
       *reg16 = TBCTL_CTRMODE_FREEZE | (NearCLKDIV << 10) | (NearHSPCLKDIV << 7);

       /*  setting duty A and duty B */
       reg16=(void*)epwm_ptr[PWMID] +EPWM_CMPB;
       *reg16 =(unsigned short)((float)NearTBPRD * dutyB);

       reg16=(void*)epwm_ptr[PWMID] +EPWM_CMPA;
       *reg16 =(unsigned short)((float)NearTBPRD * dutyA);

       reg16=(void*)epwm_ptr[PWMID] +EPWM_TBPRD;
       *reg16 =(unsigned short)NearTBPRD;

       /* reset time base counter */
       reg16 = (void *)epwm_ptr[PWMID] + EPWM_TBCNT;
       *reg16 = 0;
   }
   return 1;
}
/* ----------------------------------------------------------------------------------------------- */
/* Enable/Disable ehrPWM module
 *      @param PWMID    : PWMSS number , 0~2
 *
 *      @return         : void
 *
 *      @example        : BBBIO_PWMSS_Enable(0) ;// Enable PWMSS 0
 */

void BBBIO_ehrPWM_Enable(unsigned int PWMSS_ID)
{
   volatile unsigned short *reg16 ;

   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLA;
   *reg16 = 0x2 | ( 0x3 << 4) ;
       
   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLB;
   *reg16 = 0x2 | ( 0x3 << 8) ;

   reg16 = (void *)epwm_ptr[PWMSS_ID] + EPWM_TBCNT;
   *reg16 = 0;

        reg16=(void *)epwm_ptr[PWMSS_ID] + EPWM_TBCTL;
   *reg16 &= ~0x3;
}

void BBBIO_ehrPWM_Disable(unsigned int PWMSS_ID)
{
   volatile unsigned short *reg16 ;
        reg16=(void *)epwm_ptr[PWMSS_ID] + EPWM_TBCTL;
        *reg16 |= 0x3;

   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLA;
   *reg16 = 0x1 | ( 0x3 << 4) ;
       
   reg16=(void*)epwm_ptr[PWMSS_ID] +EPWM_AQCTLB;
   *reg16 = 0x1 | ( 0x3 << 8) ;

   reg16 = (void *)epwm_ptr[PWMSS_ID] + EPWM_TBCNT;
   *reg16 = 0;
}
//--------------------------------------------------------