-
Notifications
You must be signed in to change notification settings - Fork 20
/
port.h
137 lines (116 loc) · 3.58 KB
/
port.h
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
#ifndef PORT_H
#define PORT_H
#include "dev_data.h"
#include "features.h"
#if defined(N_PORT)
typedef struct {
uint8_t adr;
uint8_t flags;
} port_t;
extern port_t ports[];
/* _port->addr format:
* Bits 7-4 identifes the port (A=0, B=1, ...)
* Bits 3-1 bits are bit position in the port. */
#ifdef __AVR_ATmega8__
/* Mega8 layout:
* 0x2D, 0x2E, 0x2F (irrelevant, there is no port A)
* 0x30 PIND, 0x31 DDRD, 0x32 PORTD
* 0x33 PINC, 0x34 DDRC, 0x35 PORTC
* 0x36 PINB, 0x37 DDRB, 0x38 PORTB
*/
#define _P_REGS(addr) \
uint8_t _i=3*(4-(adr>>3)); \
uint8_t *pin __attribute__((unused)) = (uint8_t *)(0x2D+_i); \
uint8_t *ddr __attribute__((unused)) = (uint8_t *)(0x2D+_i+1); \
uint8_t *port __attribute__((unused)) = (uint8_t *)(0x2D+_i+2);
#else
/* Mega48/88/168 and others have
* 0x20, 0x21, 0x22 Reserved (there is no port A)
* 0x23 PINB, 0x24 DDRB, 0x25 PORTB
* 0x26 PINC, 0x27 DDRC, 0x28 PORTC
* 0x29 PIND, 0x2A DDRD, 0x2B PORTD
*/
#define _P_REGS(addr) \
uint8_t _i=3*(adr>>3); \
uint8_t *pin __attribute__((unused)) = (uint8_t *)(0x20+_i); \
uint8_t *ddr __attribute__((unused)) = (uint8_t *)(0x20+_i+1); \
uint8_t *port __attribute__((unused)) = (uint8_t *)(0x20+_i+2);
#endif
#define _P_VARS(_port) \
uint8_t flg __attribute__((unused)) = _port->flags; \
uint8_t adr = _port->adr; \
_P_REGS(addr); \
adr = 1<<(adr & 0x07);
#define _P_GET(_reg) (!!(*_reg & adr))
#define _P_SET(_reg,_val) do { \
if(_val) \
*_reg |= adr; \
else \
*_reg &=~adr; \
} while(0);
// the first two bits are used for port_out_t, i.e. PO_* constants. Hardcoded.
#define PFLG_ALERT (1<<2) // alert when port changes externally
#define PFLG_ALT (1<<3) // switch H/Z and L/pull-up; default: H/L and Z/pull-up
#define PFLG_ALT2 (1<<4) // switch H/pullup and L/Z
#define PFLG_POLL (1<<5) // change has been reported
#define PFLG_CHANGED (1<<6) // pin change
#define PFLG_CURRENT (1<<7) // last-seen value. Hardcoded.
typedef enum {
PI_OFF=0, PI_ON=1
} port_in_t;
typedef enum { // do not change: bit values are user in port.c
PO_OFF=0, PO_ON=1, PO_Z=2, PO_PULLUP=3
} port_out_t;
// actual port-pin state
static inline port_in_t port_get_in(port_t *portp) {
_P_VARS(portp)
return _P_GET(pin);
}
// read intended port state from registers
static inline port_out_t port_get_out(port_t *portp) {
_P_VARS(portp)
return _P_GET(port) | (!_P_GET(ddr)<<1) ;
}
// set intended port state
static inline void port_set_out(port_t *portp, port_out_t state) {
_P_VARS(portp)
_P_SET(port, state&1);
_P_SET(ddr,!(state&2));
portp->flags = (portp->flags&~PFLG_CURRENT) | (state<<7);
}
// update flags based on current port state
void port_check(port_t *pp);
// Set port to 0/1 according to mode (PFLG_ALT*). This is harder than it seems.
void port_set(port_t *portp, char val);
static inline char port_changed(port_t *portp) {
return portp->flags & PFLG_CHANGED;
}
/* Number of highest port that has a change +1 */
extern uint8_t port_changed_cache;
/* Note whether a port has changed */
static inline char port_has_changed(port_t *portp) {
uint8_t flg = portp->flags;
if (flg & PFLG_CHANGED) {
flg |= PFLG_POLL;
flg &=~ PFLG_CHANGED;
portp->flags = flg;
return 1;
}
return 0;
}
static inline void port_pre_send (port_t *portp) {
(void)port_has_changed(portp);
}
static inline void port_post_send (port_t *portp) {
uint8_t flg = portp->flags;
if(flg & PFLG_POLL) {
if (flg & PFLG_CHANGED)
flg &=~PFLG_CHANGED;
else
flg &=~PFLG_POLL;
portp->flags = flg;
}
}
#else // no i/o
#endif // any inputs or outputs at all
#endif // port_h