1 /*
2    minimal_gpio.c
3    2019-07-03
4    Public Domain
5 
6 	Original Site: http://abyz.me.uk/rpi/pigpio/examples.html
7 
8 */
9 
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 
19 static volatile uint32_t piPeriphBase = 0x20000000;
20 
21 static volatile int pi_is_2711 = 0;
22 
23 #define SYST_BASE  (piPeriphBase + 0x003000)
24 #define DMA_BASE   (piPeriphBase + 0x007000)
25 #define CLK_BASE   (piPeriphBase + 0x101000)
26 #define GPIO_BASE  (piPeriphBase + 0x200000)
27 #define UART0_BASE (piPeriphBase + 0x201000)
28 #define PCM_BASE   (piPeriphBase + 0x203000)
29 #define SPI0_BASE  (piPeriphBase + 0x204000)
30 #define I2C0_BASE  (piPeriphBase + 0x205000)
31 #define PWM_BASE   (piPeriphBase + 0x20C000)
32 #define BSCS_BASE  (piPeriphBase + 0x214000)
33 #define UART1_BASE (piPeriphBase + 0x215000)
34 #define I2C1_BASE  (piPeriphBase + 0x804000)
35 #define I2C2_BASE  (piPeriphBase + 0x805000)
36 #define DMA15_BASE (piPeriphBase + 0xE05000)
37 
38 #define DMA_LEN   0x1000 /* allow access to all channels */
39 #define CLK_LEN   0xA8
40 #define GPIO_LEN  0xF4
41 #define SYST_LEN  0x1C
42 #define PCM_LEN   0x24
43 #define PWM_LEN   0x28
44 #define I2C_LEN   0x1C
45 #define BSCS_LEN  0x40
46 
47 #define GPSET0 7
48 #define GPSET1 8
49 
50 #define GPCLR0 10
51 #define GPCLR1 11
52 
53 #define GPLEV0 13
54 #define GPLEV1 14
55 
56 #define GPPUD     37
57 #define GPPUDCLK0 38
58 #define GPPUDCLK1 39
59 
60 /* BCM2711 has different pulls */
61 
62 #define GPPUPPDN0 57
63 #define GPPUPPDN1 58
64 #define GPPUPPDN2 59
65 #define GPPUPPDN3 60
66 
67 #define SYST_CS  0
68 #define SYST_CLO 1
69 #define SYST_CHI 2
70 
71 static volatile uint32_t  *gpioReg = MAP_FAILED;
72 static volatile uint32_t  *systReg = MAP_FAILED;
73 static volatile uint32_t  *bscsReg = MAP_FAILED;
74 
75 #define PI_BANK (gpio>>5)
76 #define PI_BIT  (1<<(gpio&0x1F))
77 
78 /* gpio modes. */
79 
80 #define PI_INPUT  0
81 #define PI_OUTPUT 1
82 #define PI_ALT0   4
83 #define PI_ALT1   5
84 #define PI_ALT2   6
85 #define PI_ALT3   7
86 #define PI_ALT4   3
87 #define PI_ALT5   2
88 
gpioSetMode(unsigned gpio,unsigned mode)89 void gpioSetMode(unsigned gpio, unsigned mode)
90 {
91    int reg, shift;
92 
93    reg   =  gpio/10;
94    shift = (gpio%10) * 3;
95 
96    gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
97 }
98 
gpioGetMode(unsigned gpio)99 int gpioGetMode(unsigned gpio)
100 {
101    int reg, shift;
102 
103    reg   =  gpio/10;
104    shift = (gpio%10) * 3;
105 
106    return (*(gpioReg + reg) >> shift) & 7;
107 }
108 
109 /* Values for pull-ups/downs off, pull-down and pull-up. */
110 
111 #define PI_PUD_OFF  0
112 #define PI_PUD_DOWN 1
113 #define PI_PUD_UP   2
114 
gpioSetPullUpDown(unsigned gpio,unsigned pud)115 void gpioSetPullUpDown(unsigned gpio, unsigned pud)
116 {
117    int shift = (gpio & 0xf) << 1;
118    uint32_t bits;
119    uint32_t pull = 0;
120 
121    if (pi_is_2711)
122    {
123       switch (pud)
124       {
125          case PI_PUD_OFF:  pull = 0; break;
126          case PI_PUD_UP:   pull = 1; break;
127          case PI_PUD_DOWN: pull = 2; break;
128       }
129 
130       bits = *(gpioReg + GPPUPPDN0 + (gpio>>4));
131       bits &= ~(3 << shift);
132       bits |= (pull << shift);
133       *(gpioReg + GPPUPPDN0 + (gpio>>4)) = bits;
134    }
135    else
136    {
137       *(gpioReg + GPPUD) = pud;
138 
139       usleep(20);
140 
141       *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT;
142 
143       usleep(20);
144 
145       *(gpioReg + GPPUD) = 0;
146 
147       *(gpioReg + GPPUDCLK0 + PI_BANK) = 0;
148    }
149 }
150 
gpioRead(unsigned gpio)151 int gpioRead(unsigned gpio)
152 {
153    if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1;
154    else                                         return 0;
155 }
156 
gpioWrite(unsigned gpio,unsigned level)157 void gpioWrite(unsigned gpio, unsigned level)
158 {
159    if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
160    else            *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
161 }
162 
gpioTrigger(unsigned gpio,unsigned pulseLen,unsigned level)163 void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level)
164 {
165    if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
166    else            *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
167 
168    usleep(pulseLen);
169 
170    if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
171    else            *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
172 }
173 
174 /* Bit (1<<x) will be set if gpio x is high. */
175 
gpioReadBank1(void)176 uint32_t gpioReadBank1(void) { return (*(gpioReg + GPLEV0)); }
gpioReadBank2(void)177 uint32_t gpioReadBank2(void) { return (*(gpioReg + GPLEV1)); }
178 
179 /* To clear gpio x bit or in (1<<x). */
180 
gpioClearBank1(uint32_t bits)181 void gpioClearBank1(uint32_t bits) { *(gpioReg + GPCLR0) = bits; }
gpioClearBank2(uint32_t bits)182 void gpioClearBank2(uint32_t bits) { *(gpioReg + GPCLR1) = bits; }
183 
184 /* To set gpio x bit or in (1<<x). */
185 
gpioSetBank1(uint32_t bits)186 void gpioSetBank1(uint32_t bits) { *(gpioReg + GPSET0) = bits; }
gpioSetBank2(uint32_t bits)187 void gpioSetBank2(uint32_t bits) { *(gpioReg + GPSET1) = bits; }
188 
gpioHardwareRevision(void)189 unsigned gpioHardwareRevision(void)
190 {
191    static unsigned rev = 0;
192 
193    FILE *filp;
194    char buf[512];
195    char term;
196    int chars=4; /* number of chars in revision string */
197 
198    filp = fopen ("/proc/cpuinfo", "r");
199 
200    if (filp != NULL)
201    {
202       while (fgets(buf, sizeof(buf), filp) != NULL)
203       {
204          if (!strncasecmp("revision", buf, 8))
205          {
206             if (sscanf(buf+strlen(buf)-(chars+1),
207                "%x%c", &rev, &term) == 2)
208             {
209                if (term != '\n') rev = 0;
210                else rev &= 0xFFFFFF; /* mask out warranty bit */
211             }
212          }
213       }
214 
215       fclose(filp);
216    }
217 
218    filp = fopen("/proc/device-tree/soc/ranges" , "rb");
219 
220    if (filp != NULL)
221    {
222       if (fread(buf, 1, sizeof(buf), filp) >= 8)
223       {
224          piPeriphBase = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
225          if (!piPeriphBase)
226             piPeriphBase = buf[8]<<24 | buf[9]<<16 | buf[10]<<8 | buf[11];
227 
228          if (piPeriphBase == 0xFE000000) pi_is_2711 = 1;
229       }
230       fclose(filp);
231    }
232 
233    return rev;
234 }
235 
236 /* Returns the number of microseconds after system boot. Wraps around
237    after 1 hour 11 minutes 35 seconds.
238 */
239 
gpioTick(void)240 uint32_t gpioTick(void) { return systReg[SYST_CLO]; }
241 
242 
243 /* Map in registers. */
244 
initMapMem(int fd,uint32_t addr,uint32_t len)245 static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
246 {
247     return (uint32_t *) mmap(0, len,
248        PROT_READ|PROT_WRITE|PROT_EXEC,
249        MAP_SHARED|MAP_LOCKED,
250        fd, addr);
251 }
252 
gpioInitialise(void)253 int gpioInitialise(void)
254 {
255    int fd;
256 
257    gpioHardwareRevision(); /* sets rev and peripherals base address */
258 
259    fd = open("/dev/mem", O_RDWR | O_SYNC) ;
260 
261    if (fd < 0)
262    {
263       fprintf(stderr,
264          "This program needs root privileges.  Try using sudo\n");
265       return -1;
266    }
267 
268    gpioReg  = initMapMem(fd, GPIO_BASE,  GPIO_LEN);
269    systReg  = initMapMem(fd, SYST_BASE,  SYST_LEN);
270    bscsReg  = initMapMem(fd, BSCS_BASE,  BSCS_LEN);
271 
272    close(fd);
273 
274    if ((gpioReg == MAP_FAILED) ||
275        (systReg == MAP_FAILED) ||
276        (bscsReg == MAP_FAILED))
277    {
278       fprintf(stderr,
279          "Bad, mmap failed\n");
280       return -1;
281    }
282    return 0;
283 }
284