2 * \brief Interface pro ovladani pinu a pwm na raspberryPi.
5 * \author Martin Prudek
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.
28 #include <sys/ioctl.h>
33 #define BCM_PASSWORD 0x5A000000
38 #define PI_GPIO_MASK (0xFFFFFFC0)
39 #define WPI_MODE_UNINITIALISED 0
40 #define WPI_MODE_PINS 1
44 /* pouzite GPIO piny */
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)
58 #define PAGE_SIZE (4*1024)
59 #define BLOCK_SIZE (4*1024)
61 // Word offsets into the PWM control region
70 // Clock regsiter offsets
72 #define PWMCLK_CNTL 40
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
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
93 // Locals to hold pointers to the hardware
95 static volatile uint32_t *gpio ;
96 static volatile uint32_t *pwm ;
97 static volatile uint32_t *clk ;
98 static volatile uint32_t *pads ;
100 static int initialised = WPI_MODE_UNINITIALISED ;
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
108 static uint8_t gpioToGPFSEL[] =
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,
120 // Define the shift up for the 3 bits per pin in each GPFSEL port
122 static uint8_t gpioToShift[] =
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,
133 // (Word) offset to the GPIO Set registers for each GPIO pin
135 static uint8_t gpioToGPSET[] =
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,
142 // (Word) offset to the GPIO Clear registers for each GPIO pin
144 static uint8_t gpioToGPCLR[] =
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,
152 // (Word) offset to the GPIO Input level registers for each GPIO pin
154 static uint8_t gpioToGPLEV[] =
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,
161 // GPIO Pin pull up/down register
169 // the ALT value to put a GPIO pin into PWM mode
171 static uint8_t gpioToPwmALT [] =
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
183 // The port value to put a GPIO pin into PWM mode
185 static uint8_t gpioToPwmPort [] =
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
199 // (word) Offsets to the clock Control and Divisor register
201 static uint8_t gpioToClkCon [] =
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
213 static uint8_t gpioToClkDiv [] =
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
229 #define PWM_MODE_MS 0
230 #define PWM_MODE_BAL 1
232 /*****************************************************************************/
233 /****************** implementace rpin.h **************************************/
235 /* Funkce pro nativni ovladani pinu, pwm a hodin */
237 /*****************************************************************************/
240 * inicializuje pouziti GPIO pinu...
241 * (namapuje registry DMA)
243 int initialise (void)
246 int model, rev, mem, maker, overVolted ;
249 printf("Must be root. (Did you forget sudo?)\n") ;
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");
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") ;
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");
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");
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");
292 * \brief Initialize gpclk.
294 int initClock(enum ClkSource source, int divI, int divF)
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
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 */
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;
331 clkSrc = src[source];
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)
340 clk[CLK_GP0_CTL] = CLK_PASSWD | CLK_CTL_KILL;
342 /* wait for clock to stop */
344 while (clk[CLK_GP0_CTL] & CLK_CTL_BUSY){
347 #define CLK_DIV_DIVI(x) ((x)<<12)
348 #define CLK_DIV_DIVF(x) ((x)<< 0)
350 clk[CLK_GP0_DIV] = (CLK_PASSWD | CLK_DIV_DIVI(divI) | CLK_DIV_DIVF(divF));
354 clk[CLK_GP0_CTL] = (CLK_PASSWD | CLK_CTL_MASH(MASH) | CLK_CTL_SRC(clkSrc));
358 clk[CLK_GP0_CTL] |= (CLK_PASSWD | CLK_CTL_ENAB);
361 int termClock(int clock)
363 int ctl[] = {CLK_GP0_CTL, CLK_GP2_CTL};
367 if ((clock < 0) || (clock > 1)) return -1;
371 clk[clkCtl] = CLK_PASSWD | CLK_CTL_KILL;
373 /* wait for clock to stop */
375 while (clk[clkCtl] & CLK_CTL_BUSY){
382 * \brief A different approach to set gpio mode.
384 void gpioSetMode(unsigned gpio_n, unsigned mode)
389 shift = (gpio_n%10) * 3;
391 gpio[reg] = (gpio[reg] & ~(7<<shift)) | (mode<<shift);
396 * Select the native "balanced" mode, or standard mark:space mode
399 void pwmSetMode (int mode){
401 if (mode == PWM_MODE_MS){
402 *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE | PWM0_MS_MODE | PWM1_MS_MODE ;
404 *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
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.
415 void pwmSetRange (unsigned int range){
417 *(pwm + PWM0_RANGE) = range ; usleep(10) ;
418 *(pwm + PWM1_RANGE) = range ; usleep(10) ;
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
428 void pwmSetClock (int divisor){
429 uint32_t pwm_control ;
432 pwm_control = *(pwm + PWM_CONTROL) ; // preserve PWM_CONTROL
434 // We need to stop PWM prior to stopping PWM clock in MS mode otherwise BUSY
437 *(pwm + PWM_CONTROL) = 0 ; // Stop PWM
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.
445 *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01 ; // Stop PWM Clock
446 usleep(110) ; // prevents clock going sloooow
448 while ((*(clk + PWMCLK_CNTL) & 0x80) != 0) // Wait for clock to be !BUSY
451 *(clk + PWMCLK_DIV) = BCM_PASSWORD | (divisor << 12) ;
453 *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // Start PWM clock
454 *(pwm + PWM_CONTROL) = pwm_control ; // restore PWM_CONTROL
461 * \brief nastavi mod dnaeho pinu
462 * \param pin [in] cislo GPIO pinu
463 * \param mode [in] mod pinu -> in/out/altx
465 * Nastavi pin jako vstupni / vystupni, pripadne nastavi slternativni funkci.
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;
472 fSel = gpioToGPFSEL[pin] ;
473 shift = gpioToShift [pin] ;
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)
481 if ((alt = gpioToPwmALT [pin]) == 0) // Not a hardware capable PWM pin
484 // Set pin to PWM mode
486 *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ;
487 usleep(110) ; // See comments in pwmSetClockWPi
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
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
503 int digitalRead (int pin){
505 if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin
508 if (!initialised) return LOW;
510 if ((*(gpio + gpioToGPLEV [pin]) & (1 << (pin & 31))) != 0)
521 * \brief zapise logickou uroven na dany pin
522 * \param pin [in] cislo GPIO pinu
523 * \param value [in] logicka uroven 0/1
525 void digitalWrite (int pin, int value){
526 if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin
528 if (!initialised) return;
530 *(gpio + gpioToGPCLR [pin]) = 1 << (pin & 31) ;
532 *(gpio + gpioToGPSET [pin]) = 1 << (pin & 31) ;
538 * nastavi hodnotu pwm
539 * \param pin[in] cislo GPIO PWM pinu
540 * \param value[int] sirka plneni pwm (max=1024)
542 void pwmWrite (int pin, int value){
543 if ((pin & PI_GPIO_MASK) == 0) // On-Board Pin
545 if (!initialised) return;
546 *(pwm + gpioToPwmPort[pin]) = value ;
549 /*****************************************************************************/
550 /************ implementace pwm.h *********************************************/
551 /*****************************************************************************/
554 * nastavi a pripadne prevrati polaitu pwm pro pohon motoru
555 * \param width[in] sirka plneni (max=1024)
557 void pwm_width(int width){
559 digitalWrite(PWM_DIR, 0);
560 pwmWrite(PWM_WIDTH, width) ;
563 digitalWrite(PWM_DIR, 1);
564 pwmWrite(PWM_WIDTH, -width) ;
569 * \brief Inicializuje pwm.
572 if (initialise() < 0) {
573 fprintf(stderr, "Unable to setup: %s\n", strerror(errno));
577 pinMode(PWM_WIDTH, PWM_OUTPUT) ;
578 pinMode(PWM_DIR, OUTPUT) ;
583 * \brief Odinicializuje pwm.
587 pinMode (PWM_WIDTH, INPUT);
588 pinMode (PWM_DIR, INPUT);