-
Notifications
You must be signed in to change notification settings - Fork 2
/
serial.c
175 lines (156 loc) · 3.4 KB
/
serial.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
/*
sample UART software
transmit serial data at 9600,N,8,1
code for avr-gcc
ATTiny85 at 8 MHz
code in public domain
*/
#define F_CPU 8000000UL
#define USE_PRINTF
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // clear bit
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // set bit
/* ATTiny85 IO pins
___^___
-|PB5 VCC|-
LED -|PB3 PB2|-
serial out -|PB4 PB1|-
-|GND PB0|-
-------
*/
/* prototypes */
// main routines
void setup(void);
void loop(void);
int main(void);
// misc routines
void init_printf(void);
int serial_putc(char c, FILE *file);
void serial_write(uint8_t tx_byte);
uint64_t millis(void);
/* some vars */
volatile uint64_t _millis = 0;
volatile uint16_t _1000us = 0;
uint64_t old_millis = 0;
// must be volatile (change and test in main and ISR)
volatile uint8_t tx_buzy = 0;
volatile uint8_t bit_index;
volatile uint8_t _tx_buffer;
/*** ISR ***/
// compare match interrupt service for OCR0A
// call every 103us
ISR(TIM0_COMPA_vect) {
// software UART
// send data
if (tx_buzy) {
if (bit_index == 0) {
// start bit (= 0)
cbi(PORTB, PB4);
} else if (bit_index <=8) {
// LSB to MSB
if (_tx_buffer & 1) {
sbi(PORTB, PB4);
} else {
cbi(PORTB, PB4);
}
_tx_buffer >>= 1;
} else if (bit_index >= 9) {
// stop bit (= 1)
sbi(PORTB, PB4);
tx_buzy = 0;
}
bit_index++;
}
// millis update
_1000us += 103;
while (_1000us > 1000) {
_millis++;
_1000us -= 1000;
}
}
/*** UART routines ***/
// send serial data to software UART, block until UART buzy
void serial_write(uint8_t tx_byte) {
while(tx_buzy);
bit_index = 0;
_tx_buffer = tx_byte;
tx_buzy = 1;
}
#ifdef USE_PRINTF
/*** connect software UART to stdio.h ***/
void init_printf() {
fdevopen(&serial_putc, 0);
}
int serial_putc(char c, FILE *file) {
serial_write(c);
return c;
}
#else
void serial_print(const char *str) {
uint8_t i;
for (i = 0; str[i] != 0; i++) {
serial_write(str[i]);
}
}
#endif
/*** misc routines ***/
// safe access to millis counter
uint64_t millis() {
uint64_t m;
cli();
m = _millis;
sei();
return m;
}
/*** main routines ***/
void setup(void) {
// LED IO
sbi(DDRB, PB3); // set LED pin as output
sbi(PORTB, PB3); // turn the LED on
// Software UART IO
sbi(DDRB, PB4); // PB4 as output
sbi(PORTB, PB4); // serial idle level is '1'
/* interrup setup */
// call ISR(TIM0_COMPA_vect) every 103us (for 9600 bauds)
// set CTC mode : clear timer on comapre match
// -> reset TCNTO (timer 0) when TCNTO == OCR0A
sbi(TCCR0A, WGM01);
// prescaler : clk/8 (1 tic = 1us for 8MHz clock)
sbi(TCCR0B, CS01);
// compare register A at 103 us
OCR0A = 103;
// interrupt on compare match OCROA == TCNT0
sbi(TIMSK, OCIE0A);
// Enable global interrupts
sei();
#ifdef USE_PRINTF
// init stdout = serial
init_printf();
#endif
}
void loop(void) {
// every 100ms toggle LED
if ((millis() - old_millis) > 2000) {
// Toggle Port B pin 3 output state
PORTB ^= 1<<PB3;
old_millis = millis();
#ifdef USE_PRINTF
printf("toggle LED\r\n");
#else
serial_print("toggle LED\r\n");
#endif
}
}
/*
Arduino like
*/
int main(void) {
setup();
for(;;) {
loop();
}
};