]> rtime.felk.cvut.cz Git - fpga/rpi-motor-control.git/blob - pmsm-control/test_sw/rpi_hw.c
Added testing SW in folder test_sw.
[fpga/rpi-motor-control.git] / pmsm-control / test_sw / rpi_hw.c
1 /**
2  * \brief Interface pro ovladani pinu a pwm na raspberryPi.
3  * \file rpi_hw.c
4  * \date Jan 24, 2015
5  * \author Martin Prudek
6  *
7  * Poskytuje rozhrani pro cteni a zapisovani na GPIO.
8  * Zajistuje funkcnoust PWM.
9  * Napsano pro Raspberry Pi.
10  * Inspired by wiringPi written by Gordon Henderson.
11  */
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <poll.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <time.h>
22 #include <fcntl.h>
23 #include <pthread.h>
24 #include <sys/time.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28 #include <sys/ioctl.h>
29
30 #include "rpin.h"
31 #include "pwm.h"
32
33 #define BCM_PASSWORD            0x5A000000
34
35 #ifndef NULL
36         #define NULL (void*)0
37 #endif
38 #define PI_GPIO_MASK    (0xFFFFFFC0)
39 #define WPI_MODE_UNINITIALISED  0
40 #define WPI_MODE_PINS           1
41
42
43
44 /* pouzite GPIO piny */
45 #define PWM_WIDTH       18
46 #define PWM_DIR         22
47 #define IRC_A           24
48 #define IRC_B           8
49
50 /* Bazove adresy periferii */
51 #define BCM2708_PERI_BASE                            0x20000000
52 #define GPIO_PADS               (BCM2708_PERI_BASE + 0x00100000)
53 #define CLOCK_BASE              (BCM2708_PERI_BASE + 0x00101000)
54 #define GPIO_BASE               (BCM2708_PERI_BASE + 0x00200000)
55 #define GPIO_TIMER              (BCM2708_PERI_BASE + 0x0000B000)
56 #define GPIO_PWM                (BCM2708_PERI_BASE + 0x0020C000)
57
58 #define PAGE_SIZE               (4*1024)
59 #define BLOCK_SIZE              (4*1024)
60 // PWM
61 //      Word offsets into the PWM control region
62
63 #define PWM_CONTROL 0
64 #define PWM_STATUS  1
65 #define PWM0_RANGE  4
66 #define PWM0_DATA   5
67 #define PWM1_RANGE  8
68 #define PWM1_DATA   9
69
70 //      Clock regsiter offsets
71
72 #define PWMCLK_CNTL     40
73 #define PWMCLK_DIV      41
74
75 #define PWM0_MS_MODE    0x0080  // Run in MS mode
76 #define PWM0_USEFIFO    0x0020  // Data from FIFO
77 #define PWM0_REVPOLAR   0x0010  // Reverse polarity
78 #define PWM0_OFFSTATE   0x0008  // Ouput Off state
79 #define PWM0_REPEATFF   0x0004  // Repeat last value if FIFO empty
80 #define PWM0_SERIAL     0x0002  // Run in serial mode
81 #define PWM0_ENABLE     0x0001  // Channel Enable
82
83 #define PWM1_MS_MODE    0x8000  // Run in MS mode
84 #define PWM1_USEFIFO    0x2000  // Data from FIFO
85 #define PWM1_REVPOLAR   0x1000  // Reverse polarity
86 #define PWM1_OFFSTATE   0x0800  // Ouput Off state
87 #define PWM1_REPEATFF   0x0400  // Repeat last value if FIFO empty
88 #define PWM1_SERIAL     0x0200  // Run in serial mode
89 #define PWM1_ENABLE     0x0100  // Channel Enable
90
91
92
93 // Locals to hold pointers to the hardware
94
95 static volatile uint32_t *gpio ;
96 static volatile uint32_t *pwm ;
97 static volatile uint32_t *clk ;
98 static volatile uint32_t *pads ;
99
100 static int initialised = WPI_MODE_UNINITIALISED ;
101
102
103 // gpioToGPFSEL:
104 //      Map a BCM_GPIO pin to it's Function Selection
105 //      control port. (GPFSEL 0-5)
106 //      Groups of 10 - 3 bits per Function - 30 bits per port
107
108 static uint8_t gpioToGPFSEL[] =
109 {
110   0,0,0,0,0,0,0,0,0,0,
111   1,1,1,1,1,1,1,1,1,1,
112   2,2,2,2,2,2,2,2,2,2,
113   3,3,3,3,3,3,3,3,3,3,
114   4,4,4,4,4,4,4,4,4,4,
115   5,5,5,5,5,5,5,5,5,5,
116 } ;
117
118
119 // gpioToShift
120 //      Define the shift up for the 3 bits per pin in each GPFSEL port
121
122 static uint8_t gpioToShift[] =
123 {
124   0,3,6,9,12,15,18,21,24,27,
125   0,3,6,9,12,15,18,21,24,27,
126   0,3,6,9,12,15,18,21,24,27,
127   0,3,6,9,12,15,18,21,24,27,
128   0,3,6,9,12,15,18,21,24,27,
129 } ;
130
131
132 // gpioToGPSET:
133 //      (Word) offset to the GPIO Set registers for each GPIO pin
134
135 static uint8_t gpioToGPSET[] =
136 {
137    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
138    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
139 } ;
140
141 // gpioToGPCLR:
142 //      (Word) offset to the GPIO Clear registers for each GPIO pin
143
144 static uint8_t gpioToGPCLR[] =
145 {
146   10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
147   11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
148 } ;
149
150
151 // gpioToGPLEV:
152 //      (Word) offset to the GPIO Input level registers for each GPIO pin
153
154 static uint8_t gpioToGPLEV[] =
155 {
156   13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
157   14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
158 } ;
159
160 // GPPUD:
161 //      GPIO Pin pull up/down register
162
163 #define GPPUD   37
164
165
166
167
168 // gpioToPwmALT
169 //      the ALT value to put a GPIO pin into PWM mode
170
171 static uint8_t gpioToPwmALT [] =
172 {
173           0,         0,         0,         0,         0,         0,         0,         0,       //  0 ->  7
174           0,         0,         0,         0, FSEL_ALT0, FSEL_ALT0,         0,         0,       //  8 -> 15
175           0,         0, FSEL_ALT5, FSEL_ALT5,         0,         0,         0,         0,       // 16 -> 23
176           0,         0,         0,         0,         0,         0,         0,         0,       // 24 -> 31
177           0,         0,         0,         0,         0,         0,         0,         0,       // 32 -> 39
178   FSEL_ALT0, FSEL_ALT0,         0,         0,         0, FSEL_ALT0,         0,         0,       // 40 -> 47
179           0,         0,         0,         0,         0,         0,         0,         0,       // 48 -> 55
180           0,         0,         0,         0,         0,         0,         0,         0,       // 56 -> 63
181 } ;
182 // gpioToPwmPort
183 //      The port value to put a GPIO pin into PWM mode
184
185 static uint8_t gpioToPwmPort [] =
186 {
187           0,         0,         0,         0,         0,         0,         0,         0,       //  0 ->  7
188           0,         0,         0,         0, PWM0_DATA, PWM1_DATA,         0,         0,       //  8 -> 15
189           0,         0, PWM0_DATA, PWM1_DATA,         0,         0,         0,         0,       // 16 -> 23
190           0,         0,         0,         0,         0,         0,         0,         0,       // 24 -> 31
191           0,         0,         0,         0,         0,         0,         0,         0,       // 32 -> 39
192   PWM0_DATA, PWM1_DATA,         0,         0,         0, PWM1_DATA,         0,         0,       // 40 -> 47
193           0,         0,         0,         0,         0,         0,         0,         0,       // 48 -> 55
194           0,         0,         0,         0,         0,         0,         0,         0,       // 56 -> 63
195
196 } ;
197
198 // gpioToClk:
199 //      (word) Offsets to the clock Control and Divisor register
200 /*
201 static uint8_t gpioToClkCon [] =
202 {
203          -1,        -1,        -1,        -1,        28,        30,        32,        -1,       //  0 ->  7
204          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       //  8 -> 15
205          -1,        -1,        -1,        -1,        28,        30,        -1,        -1,       // 16 -> 23
206          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       // 24 -> 31
207          28,        -1,        28,        -1,        -1,        -1,        -1,        -1,       // 32 -> 39
208          -1,        -1,        28,        30,        28,        -1,        -1,        -1,       // 40 -> 47
209          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       // 48 -> 55
210          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       // 56 -> 63
211 } ;
212
213 static uint8_t gpioToClkDiv [] =
214 {
215          -1,        -1,        -1,        -1,        29,        31,        33,        -1,       //  0 ->  7
216          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       //  8 -> 15
217          -1,        -1,        -1,        -1,        29,        31,        -1,        -1,       // 16 -> 23
218          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       // 24 -> 31
219          29,        -1,        29,        -1,        -1,        -1,        -1,        -1,       // 32 -> 39
220          -1,        -1,        29,        31,        29,        -1,        -1,        -1,       // 40 -> 47
221          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       // 48 -> 55
222          -1,        -1,        -1,        -1,        -1,        -1,        -1,        -1,       // 56 -> 63
223 } ;
224 */
225
226
227 // PWM
228
229 #define PWM_MODE_MS             0
230 #define PWM_MODE_BAL            1
231
232 /*****************************************************************************/
233 /****************** implementace rpin.h **************************************/
234 /*                                                                           */
235 /* Funkce pro nativni ovladani pinu, pwm a hodin                             */
236 /*                                                                           */
237 /*****************************************************************************/
238
239 /**
240  * inicializuje pouziti GPIO pinu...
241  * (namapuje registry DMA)
242  */
243 int initialise (void)
244 {
245         int   fd ;
246         int   model, rev, mem, maker, overVolted ;
247
248         if (geteuid() != 0){
249                 printf("Must be root. (Did you forget sudo?)\n") ;
250                 return 1;
251         }
252
253         // Open the master /dev/memory device
254         if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0){
255                 printf(" Unable to open /dev/mem: %s\n");
256                 return 1;
257         }
258
259         // GPIO:
260         gpio = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_BASE) ;
261         if ((int32_t)gpio == -1){
262                 printf("mmap (GPIO) failed: %s\n") ;
263                 return 1;
264         }
265
266
267         // PWM
268         pwm = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_PWM) ;
269         if ((int32_t)pwm == -1){
270                 printf("mmap (PWM) failed: %s\n");
271                 return 1;
272         }
273
274         // Clock control (needed for PWM)
275         clk = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, CLOCK_BASE) ;
276         if ((int32_t)clk == -1){
277                 printf("mmap (CLOCK) failed: %s\n");
278                 return 1;
279         }
280
281         pads = (uint32_t *)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_PADS) ;
282         if ((int32_t)pads == -1){
283                 printf("mmap (PADS) failed: %s\n");
284                 return 1;
285         }
286         close(fd);
287         initialised = 1 ;
288
289         return 0 ;
290 }
291 /**
292  * \brief Initialize gpclk.
293  */
294 int initClock(enum ClkSource source, int divI, int divF)
295 {
296         const int MASH = 0;
297         #define CLK_GP0_CTL 28
298         #define CLK_GP0_DIV 29
299         #define CLK_GP1_CTL 30
300         #define CLK_GP1_DIV 31
301         #define CLK_GP2_CTL 32
302         #define CLK_GP2_DIV 33
303
304         #define CLK_CTL_SRC_OSC  1  /* 19.2 MHz */
305         #define CLK_CTL_SRC_PLLC 5  /* 1000 MHz */
306         #define CLK_CTL_SRC_PLLD 6  /*  500 MHz */
307         #define CLK_CTL_SRC_HDMI 7  /*  216 MHz */
308
309         int src[] ={
310                 CLK_CTL_SRC_PLLD,
311                 CLK_CTL_SRC_OSC,
312                 CLK_CTL_SRC_HDMI,
313                 CLK_CTL_SRC_PLLC
314         };
315
316         int clkSrc;
317         uint32_t setting;
318
319         if (!initialised){
320                 return -1;
321         }
322
323
324         if ((source < 0) || (source > 3 ))   return -2;
325         if ((divI   < 2) || (divI   > 4095)) return -3;
326         if ((divF   < 0) || (divF   > 4095)) return -4;
327         if ((MASH   < 0) || (MASH   > 3))    return -5;
328
329
330
331         clkSrc = src[source];
332
333         #define CLK_PASSWD  (0x5A<<24)
334         #define CLK_CTL_MASH(x)((x)<<9)
335         #define CLK_CTL_BUSY    (1 <<7)
336         #define CLK_CTL_KILL    (1 <<5)
337         #define CLK_CTL_ENAB    (1 <<4)
338         #define CLK_CTL_SRC(x) ((x)<<0)
339
340         clk[CLK_GP0_CTL] = CLK_PASSWD | CLK_CTL_KILL;
341
342         /* wait for clock to stop */
343
344         while (clk[CLK_GP0_CTL] & CLK_CTL_BUSY){
345                 usleep(10);
346         }
347         #define CLK_DIV_DIVI(x) ((x)<<12)
348         #define CLK_DIV_DIVF(x) ((x)<< 0)
349
350         clk[CLK_GP0_DIV] = (CLK_PASSWD | CLK_DIV_DIVI(divI) | CLK_DIV_DIVF(divF));
351
352         usleep(10);
353
354         clk[CLK_GP0_CTL] = (CLK_PASSWD | CLK_CTL_MASH(MASH) | CLK_CTL_SRC(clkSrc));
355
356         usleep(10);
357
358         clk[CLK_GP0_CTL] |= (CLK_PASSWD | CLK_CTL_ENAB);
359         return 0;
360 }
361 int termClock(int clock)
362 {
363         int ctl[] = {CLK_GP0_CTL, CLK_GP2_CTL};
364
365         int clkCtl;
366
367         if ((clock  < 0) || (clock  > 1))    return -1;
368
369         clkCtl = ctl[clock];
370
371         clk[clkCtl] = CLK_PASSWD | CLK_CTL_KILL;
372
373         /* wait for clock to stop */
374
375         while (clk[clkCtl] & CLK_CTL_BUSY){
376                 usleep(10);
377         }
378
379         return 0;
380 }
381 /**
382  * \brief A different approach to set gpio mode.
383  */
384 void gpioSetMode(unsigned gpio_n, unsigned mode)
385 {
386         int reg, shift;
387
388         reg   =  gpio_n/10;
389         shift = (gpio_n%10) * 3;
390
391         gpio[reg] = (gpio[reg] & ~(7<<shift)) | (mode<<shift);
392 }
393
394 /**
395  * pwmSetMode:
396  *      Select the native "balanced" mode, or standard mark:space mode
397  */
398
399 void pwmSetMode (int mode){
400         if (initialised){
401                 if (mode == PWM_MODE_MS){
402                         *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE | PWM0_MS_MODE | PWM1_MS_MODE ;
403                 }else{
404                         *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
405                 }
406         }
407 }
408
409 /**
410  * pwmSetRange:
411  *      Set the PWM range register. We set both range registers to the same
412  *      value. If you want different in your own code, then write your own.
413  */
414
415 void pwmSetRange (unsigned int range){
416         if (initialised){
417                 *(pwm + PWM0_RANGE) = range ; usleep(10) ;
418                 *(pwm + PWM1_RANGE) = range ; usleep(10) ;
419         }
420 }
421 /**
422  * pwmSetClock:
423  *      Set/Change the PWM clock. Originally Drogon's code, but changed
424  *      (for the better!) by Chris Hall, <chris@kchall.plus.com>
425  *      after further study of the manual and testing with a 'scope
426  */
427
428 void pwmSetClock (int divisor){
429         uint32_t pwm_control ;
430         divisor &= 4095 ;
431         if (initialised){
432                 pwm_control = *(pwm + PWM_CONTROL) ;            // preserve PWM_CONTROL
433
434                 // We need to stop PWM prior to stopping PWM clock in MS mode otherwise BUSY
435                 // stays high.
436
437                 *(pwm + PWM_CONTROL) = 0 ;                              // Stop PWM
438
439                 // Stop PWM clock before changing divisor. The delay after this does need to
440                 // this big (95uS occasionally fails, 100uS OK), it's almost as though the BUSY
441                 // flag is not working properly in balanced mode. Without the delay when DIV is
442                 // adjusted the clock sometimes switches to very slow, once slow further DIV
443                 // adjustments do nothing and it's difficult to get out of this mode.
444
445                 *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01 ;    // Stop PWM Clock
446                 usleep(110) ;                   // prevents clock going sloooow
447
448                 while ((*(clk + PWMCLK_CNTL) & 0x80) != 0)      // Wait for clock to be !BUSY
449                         usleep(1) ;
450
451                 *(clk + PWMCLK_DIV)  = BCM_PASSWORD | (divisor << 12) ;
452
453                 *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ;    // Start PWM clock
454                 *(pwm + PWM_CONTROL) = pwm_control ;            // restore PWM_CONTROL
455
456         }
457 }
458
459
460 /**
461  * \brief nastavi mod dnaeho pinu
462  * \param pin [in] cislo GPIO pinu
463  * \param mode [in] mod pinu -> in/out/altx
464  *
465  * Nastavi pin jako vstupni / vystupni, pripadne nastavi slternativni funkci.
466  */
467 void pinMode (int pin, int mode){
468         int    fSel, shift, alt ;
469         if ((pin & PI_GPIO_MASK) == 0){ //exituje-li pin
470                 if (!initialised) return;
471
472                 fSel    = gpioToGPFSEL[pin] ;
473                 shift   = gpioToShift [pin] ;
474
475                 if (mode == INPUT)
476                         *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) ; // Sets bits to zero = input
477                 else if (mode == OUTPUT)
478                         *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (1 << shift) ;
479                 else if (mode == PWM_OUTPUT)
480                 {
481                         if ((alt = gpioToPwmALT [pin]) == 0)    // Not a hardware capable PWM pin
482                                 return ;
483
484                         // Set pin to PWM mode
485
486                         *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ;
487                         usleep(110) ;           // See comments in pwmSetClockWPi
488
489                         pwmSetMode  (PWM_MODE_BAL) ;    // Pi default mode
490                         pwmSetRange (1024) ;            // Default range of 1024
491                         pwmSetClock (32) ;              // 19.2 / 32 = 600KHz - Also starts the PWM
492                 }
493
494         }
495
496 }
497 /**
498  * \brief Precte logickou uroven daneho pinu 0/1
499  * v pripade neicnicializovaneho GPIO nebo neexistence pinu vrati 0
500  * \param pin [in] cislo GPIO pinu
501  */
502
503 int digitalRead (int pin){
504         char c ;
505         if ((pin & PI_GPIO_MASK) == 0)          // On-Board Pin
506         {
507
508                 if (!initialised) return LOW;
509
510                 if ((*(gpio + gpioToGPLEV [pin]) & (1 << (pin & 31))) != 0)
511                         return HIGH ;
512                 else
513                         return LOW ;
514         }else{
515                 return LOW;
516         }
517 }
518
519
520 /**
521  * \brief zapise logickou uroven na dany pin
522  * \param pin [in] cislo GPIO pinu
523  * \param value [in] logicka uroven 0/1
524  */
525 void digitalWrite (int pin, int value){
526         if ((pin & PI_GPIO_MASK) == 0)          // On-Board Pin
527         {
528                 if (!initialised) return;
529                 if (value == LOW)
530                         *(gpio + gpioToGPCLR [pin]) = 1 << (pin & 31) ;
531                 else
532                         *(gpio + gpioToGPSET [pin]) = 1 << (pin & 31) ;
533         }
534 }
535
536
537 /**
538  * nastavi hodnotu pwm
539  * \param pin[in] cislo GPIO PWM pinu
540  * \param value[int] sirka plneni  pwm (max=1024)
541  */
542 void pwmWrite (int pin, int value){
543         if ((pin & PI_GPIO_MASK) == 0)          // On-Board Pin
544         {
545                 if (!initialised) return;
546                 *(pwm + gpioToPwmPort[pin]) = value ;
547         }
548 }
549 /*****************************************************************************/
550 /************ implementace pwm.h *********************************************/
551 /*****************************************************************************/
552
553 /**
554  * nastavi a pripadne prevrati polaitu pwm pro pohon motoru
555  * \param width[in] sirka plneni (max=1024)
556  */
557 void pwm_width(int width){
558         if (width >= 0) {
559                 digitalWrite(PWM_DIR, 0);
560                 pwmWrite(PWM_WIDTH, width) ;
561         }
562         else {
563                 digitalWrite(PWM_DIR, 1);
564                 pwmWrite(PWM_WIDTH, -width) ;
565         }
566 }
567
568 /**
569  * \brief Inicializuje pwm.
570  */
571 void pwm_init(){
572         if (initialise() < 0) {
573               fprintf(stderr, "Unable to setup: %s\n", strerror(errno));
574               return;
575         }
576
577         pinMode(PWM_WIDTH, PWM_OUTPUT) ;
578         pinMode(PWM_DIR, OUTPUT) ;
579         pwm_width(0);
580 }
581
582 /**
583  * \brief Odinicializuje pwm.
584  */
585 void pwm_disable(){
586         pwm_width(0);
587         pinMode (PWM_WIDTH, INPUT);
588         pinMode (PWM_DIR, INPUT);
589 }
590
591
592
593
594
595