-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgpio.c
191 lines (154 loc) · 4.48 KB
/
gpio.c
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
/**
* Copyright by Andrea Cervesato <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
**/
#include "gpio.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <fcntl.h>
// device address
#define BCM2708_BASE_DEFAULT 0x20000000
#define BCM2709_BASE_DEFAULT 0x3f000000
#define GPIO_BASE_OFFSET 0x200000
// operations registers
#define OFFSET_FSEL 0
#define OFFSET_SET 7
#define OFFSET_CLR 10
#define OFFSET_READ 13
// pages dimension
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
// number of ports
#define MAX_GPIO_PINS 7
#define CHECK_PIN(p) \
do { \
if (p < 0)\
p = 0; \
else if (p >= MAX_GPIO_PINS) \
p = MAX_GPIO_PINS - 1; \
} while(0)
// the virtual memory associated with the gpio memory
static volatile uint32_t *gpio_map;
// this array maps the gpio pin with its register on board
static int pinToGpio[7] =
{
17, 18, 27, 22, 23, 24, 25 // digital I/O
};
int rpi_gpio_setup(void)
{
int mem_fd = 0; // file descriptor
int gpio_base = 0;
int gpio_dev = 0;
uint8_t *gpio_mem = NULL; // virtual memory block
// try using /dev/mem method
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) {
printf("Gpio setup can't open /dev/mem device.\n");
return GPIO_SETUP_FAILED;
}
if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
printf("Gpio setup can't allocate space for /dev/mem device\n");
return GPIO_SETUP_FAILED;
}
if (*(uint32_t*)gpio_mem % PAGE_SIZE) {
gpio_mem += PAGE_SIZE - (*(uint32_t*)gpio_mem % PAGE_SIZE);
}
gpio_dev = BCM2709_BASE_DEFAULT;
gpio_base = gpio_dev + GPIO_BASE_OFFSET;
gpio_map = (uint32_t*)mmap((void*)gpio_mem,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_FIXED,
mem_fd,
gpio_base);
if (*(uint32_t*)gpio_map < 0) {
printf("Gpio setup can't access to virtual GPIO memory.\n");
return GPIO_SETUP_FAILED;
}
return GPIO_NO_ERROR;
}
int rpi_gpio_cleanup(void)
{
munmap((void*)gpio_map, BLOCK_SIZE);
return GPIO_NO_ERROR;
}
// direction: 0 is IN, 1 is OUT
static void rpio_gpio_inner_set_direction(int pin, int direction)
{
int offset = 0;
int shift = 0;
int port = 0;
CHECK_PIN(pin);
port = pinToGpio[pin];
// selection bits are organized by cluster of 10 gpio ports
offset = OFFSET_FSEL + (port / 10);
// the selection bits are 3 and they are placed one next each other
shift = (port % 10) * 3;
if (direction) { // output
*(gpio_map + offset) = (*(gpio_map + offset) & ~(7 << shift)) | (1 << shift);
} else { // input
*(gpio_map + offset) = (*(gpio_map + offset) & ~(7 << shift));
}
}
void rpi_gpio_set_input(int port)
{
rpio_gpio_inner_set_direction(port, 0);
}
void rpi_gpio_set_output(int port)
{
rpio_gpio_inner_set_direction(port, 1);
}
void rpi_gpio_write_status(int pin, int status)
{
int offset = 0;
int port = 0;
CHECK_PIN(pin);
port = pinToGpio[pin];
if (status) { // HIGH
offset = OFFSET_SET + (port / 32);
} else { // LOW
offset = OFFSET_CLR + (port / 32);
}
*(gpio_map + offset) = 1 << (port % 32);
}
int rpi_gpio_read_status(int pin)
{
int offset = 0;
int port = 0;
int value = 0;
CHECK_PIN(pin);
port = pinToGpio[pin];
offset = OFFSET_READ + (port / 32);
value = *(gpio_map + offset) & (1 << (port % 32));
// move the port bit in the less significant bit position,
// so the routine results will be 0 or 1
value = value >> port;
return value;
}
// This function sets a digital pin to up state.
// Use with caution: no bounds check on pin number line
inline void rpi_gpio_fast_up(int pin)
{
int port = pinToGpio[pin];
*(gpio_map + OFFSET_SET + (port/32)) = 1 << (port%32);
}
// This function sets a digital pin to down state
// Use with caution: no bounds check on pin number line
inline void rpi_gpio_fast_down(int pin)
{
int port = pinToGpio[pin];
*(gpio_map + OFFSET_CLR + (port/32)) = 1 << (port%32);
}
// This function returns the digital state of a gpio line.
// If returns value is 0 the status is low, up otherwise.
// Use with caution: no bounds check on pin number line
inline int rpi_gpio_fast_read(int pin)
{
int port = pinToGpio[pin];
return *(gpio_map + OFFSET_READ + (port/32)) & (1<< (port%32));
}