aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr/drivers')
-rw-r--r--platforms/avr/drivers/analog.c23
-rw-r--r--platforms/avr/drivers/analog.h3
-rw-r--r--platforms/avr/drivers/audio_pwm.h17
-rw-r--r--platforms/avr/drivers/audio_pwm_hardware.c332
-rw-r--r--platforms/avr/drivers/i2c_master.c56
-rw-r--r--platforms/avr/drivers/i2c_master.h2
-rw-r--r--platforms/avr/drivers/ps2/ps2_io.c51
-rw-r--r--platforms/avr/drivers/ps2/ps2_usart.c227
8 files changed, 686 insertions, 25 deletions
diff --git a/platforms/avr/drivers/analog.c b/platforms/avr/drivers/analog.c
index 8d299ffdb..628835cce 100644
--- a/platforms/avr/drivers/analog.c
+++ b/platforms/avr/drivers/analog.c
@@ -23,29 +23,6 @@ static uint8_t aref = ADC_REF_POWER;
23 23
24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); } 24void analogReference(uint8_t mode) { aref = mode & (_BV(REFS1) | _BV(REFS0)); }
25 25
26// Arduino compatible pin input
27int16_t analogRead(uint8_t pin) {
28#if defined(__AVR_ATmega32U4__)
29 // clang-format off
30 static const uint8_t PROGMEM pin_to_mux[] = {
31 //A0 A1 A2 A3 A4 A5
32 //F7 F6 F5 F4 F1 F0
33 0x07, 0x06, 0x05, 0x04, 0x01, 0x00,
34 //A6 A7 A8 A9 A10 A11
35 //D4 D7 B4 B5 B6 D6
36 0x20, 0x22, 0x23, 0x24, 0x25, 0x21
37 };
38 // clang-format on
39 if (pin >= 12) return 0;
40 return adc_read(pgm_read_byte(pin_to_mux + pin));
41#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
42 if (pin >= 8) return 0;
43 return adc_read(pin);
44#else
45 return 0;
46#endif
47}
48
49int16_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); } 26int16_t analogReadPin(pin_t pin) { return adc_read(pinToMux(pin)); }
50 27
51uint8_t pinToMux(pin_t pin) { 28uint8_t pinToMux(pin_t pin) {
diff --git a/platforms/avr/drivers/analog.h b/platforms/avr/drivers/analog.h
index 058882450..fa2fb0d89 100644
--- a/platforms/avr/drivers/analog.h
+++ b/platforms/avr/drivers/analog.h
@@ -22,8 +22,7 @@
22#ifdef __cplusplus 22#ifdef __cplusplus
23extern "C" { 23extern "C" {
24#endif 24#endif
25void analogReference(uint8_t mode); 25void analogReference(uint8_t mode);
26int16_t analogRead(uint8_t pin);
27 26
28int16_t analogReadPin(pin_t pin); 27int16_t analogReadPin(pin_t pin);
29uint8_t pinToMux(pin_t pin); 28uint8_t pinToMux(pin_t pin);
diff --git a/platforms/avr/drivers/audio_pwm.h b/platforms/avr/drivers/audio_pwm.h
new file mode 100644
index 000000000..d6eb3571d
--- /dev/null
+++ b/platforms/avr/drivers/audio_pwm.h
@@ -0,0 +1,17 @@
1/* Copyright 2020 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#pragma once
diff --git a/platforms/avr/drivers/audio_pwm_hardware.c b/platforms/avr/drivers/audio_pwm_hardware.c
new file mode 100644
index 000000000..df03a4558
--- /dev/null
+++ b/platforms/avr/drivers/audio_pwm_hardware.c
@@ -0,0 +1,332 @@
1/* Copyright 2016 Jack Humbert
2 * Copyright 2020 JohSchneider
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#if defined(__AVR__)
19# include <avr/pgmspace.h>
20# include <avr/interrupt.h>
21# include <avr/io.h>
22#endif
23
24#include "audio.h"
25
26extern bool playing_note;
27extern bool playing_melody;
28extern uint8_t note_timbre;
29
30#define CPU_PRESCALER 8
31
32/*
33 Audio Driver: PWM
34
35 drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
36
37 the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
38 and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
39
40 alternatively, the PWM pins on PORTB can be used as only/primary speaker
41*/
42
43#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
44# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
45#endif
46
47#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
48# define AUDIO1_PIN_SET
49# define AUDIO1_TIMSKx TIMSK3
50# define AUDIO1_TCCRxA TCCR3A
51# define AUDIO1_TCCRxB TCCR3B
52# define AUDIO1_ICRx ICR3
53# define AUDIO1_WGMx0 WGM30
54# define AUDIO1_WGMx1 WGM31
55# define AUDIO1_WGMx2 WGM32
56# define AUDIO1_WGMx3 WGM33
57# define AUDIO1_CSx0 CS30
58# define AUDIO1_CSx1 CS31
59# define AUDIO1_CSx2 CS32
60
61# if (AUDIO_PIN == C6)
62# define AUDIO1_COMxy0 COM3A0
63# define AUDIO1_COMxy1 COM3A1
64# define AUDIO1_OCIExy OCIE3A
65# define AUDIO1_OCRxy OCR3A
66# define AUDIO1_PIN C6
67# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
68# elif (AUDIO_PIN == C5)
69# define AUDIO1_COMxy0 COM3B0
70# define AUDIO1_COMxy1 COM3B1
71# define AUDIO1_OCIExy OCIE3B
72# define AUDIO1_OCRxy OCR3B
73# define AUDIO1_PIN C5
74# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
75# elif (AUDIO_PIN == C4)
76# define AUDIO1_COMxy0 COM3C0
77# define AUDIO1_COMxy1 COM3C1
78# define AUDIO1_OCIExy OCIE3C
79# define AUDIO1_OCRxy OCR3C
80# define AUDIO1_PIN C4
81# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
82# endif
83#endif
84
85#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
86# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
87#endif
88
89#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
90# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
91#endif
92
93#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
94# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
95#endif
96
97#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
98# define AUDIO2_PIN_SET
99# define AUDIO2_TIMSKx TIMSK1
100# define AUDIO2_TCCRxA TCCR1A
101# define AUDIO2_TCCRxB TCCR1B
102# define AUDIO2_ICRx ICR1
103# define AUDIO2_WGMx0 WGM10
104# define AUDIO2_WGMx1 WGM11
105# define AUDIO2_WGMx2 WGM12
106# define AUDIO2_WGMx3 WGM13
107# define AUDIO2_CSx0 CS10
108# define AUDIO2_CSx1 CS11
109# define AUDIO2_CSx2 CS12
110
111# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
112# define AUDIO2_COMxy0 COM1A0
113# define AUDIO2_COMxy1 COM1A1
114# define AUDIO2_OCIExy OCIE1A
115# define AUDIO2_OCRxy OCR1A
116# define AUDIO2_PIN B5
117# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
118# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
119# define AUDIO2_COMxy0 COM1B0
120# define AUDIO2_COMxy1 COM1B1
121# define AUDIO2_OCIExy OCIE1B
122# define AUDIO2_OCRxy OCR1B
123# define AUDIO2_PIN B6
124# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
125# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
126# define AUDIO2_COMxy0 COM1C0
127# define AUDIO2_COMxy1 COM1C1
128# define AUDIO2_OCIExy OCIE1C
129# define AUDIO2_OCRxy OCR1C
130# define AUDIO2_PIN B7
131# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
132# elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
133# pragma message "Audio support for ATmega32A is experimental and can cause crashes."
134# undef AUDIO2_TIMSKx
135# define AUDIO2_TIMSKx TIMSK
136# define AUDIO2_COMxy0 COM1A0
137# define AUDIO2_COMxy1 COM1A1
138# define AUDIO2_OCIExy OCIE1A
139# define AUDIO2_OCRxy OCR1A
140# define AUDIO2_PIN D5
141# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
142# endif
143#endif
144
145// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
146#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
147# pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
148// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
149#endif
150// -----------------------------------------------------------------------------
151
152#ifdef AUDIO1_PIN_SET
153static float channel_1_frequency = 0.0f;
154void channel_1_set_frequency(float freq) {
155 if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
156 {
157 // disable the output, but keep the pwm-ISR going (with the previous
158 // frequency) so the audio-state keeps getting updated
159 // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
160 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
161 return;
162 } else {
163 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
164 }
165
166 channel_1_frequency = freq;
167
168 // set pwm period
169 AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
170 // and duty cycle
171 AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
172}
173
174void channel_1_start(void) {
175 // enable timer-counter ISR
176 AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
177 // enable timer-counter output
178 AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
179}
180
181void channel_1_stop(void) {
182 // disable timer-counter ISR
183 AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
184 // disable timer-counter output
185 AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
186}
187#endif
188
189#ifdef AUDIO2_PIN_SET
190static float channel_2_frequency = 0.0f;
191void channel_2_set_frequency(float freq) {
192 if (freq == 0.0f) {
193 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
194 return;
195 } else {
196 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
197 }
198
199 channel_2_frequency = freq;
200
201 AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
202 AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
203}
204
205float channel_2_get_frequency(void) { return channel_2_frequency; }
206
207void channel_2_start(void) {
208 AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
209 AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
210}
211
212void channel_2_stop(void) {
213 AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
214 AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
215}
216#endif
217
218void audio_driver_initialize() {
219#ifdef AUDIO1_PIN_SET
220 channel_1_stop();
221 setPinOutput(AUDIO1_PIN);
222#endif
223
224#ifdef AUDIO2_PIN_SET
225 channel_2_stop();
226 setPinOutput(AUDIO2_PIN);
227#endif
228
229 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
230 // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
231 // OC3A -- PC6
232 // OC3B -- PC5
233 // OC3C -- PC4
234 // OC1A -- PB5
235 // OC1B -- PB6
236 // OC1C -- PB7
237
238 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
239 // OCR3A - PC6
240 // OCR3B - PC5
241 // OCR3C - PC4
242 // OCR1A - PB5
243 // OCR1B - PB6
244 // OCR1C - PB7
245
246 // Clock Select (CS3n) = 0b010 = Clock / 8
247#ifdef AUDIO1_PIN_SET
248 // initialize timer-counter
249 AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
250 AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
251#endif
252
253#ifdef AUDIO2_PIN_SET
254 AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
255 AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
256#endif
257}
258
259void audio_driver_stop() {
260#ifdef AUDIO1_PIN_SET
261 channel_1_stop();
262#endif
263
264#ifdef AUDIO2_PIN_SET
265 channel_2_stop();
266#endif
267}
268
269void audio_driver_start(void) {
270#ifdef AUDIO1_PIN_SET
271 channel_1_start();
272 if (playing_note) {
273 channel_1_set_frequency(audio_get_processed_frequency(0));
274 }
275#endif
276
277#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
278 channel_2_start();
279 if (playing_note) {
280 channel_2_set_frequency(audio_get_processed_frequency(0));
281 }
282#endif
283}
284
285static volatile uint32_t isr_counter = 0;
286#ifdef AUDIO1_PIN_SET
287ISR(AUDIO1_TIMERx_COMPy_vect) {
288 isr_counter++;
289 if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
290
291 isr_counter = 0;
292 bool state_changed = audio_update_state();
293
294 if (!playing_note && !playing_melody) {
295 channel_1_stop();
296# ifdef AUDIO2_PIN_SET
297 channel_2_stop();
298# endif
299 return;
300 }
301
302 if (state_changed) {
303 channel_1_set_frequency(audio_get_processed_frequency(0));
304# ifdef AUDIO2_PIN_SET
305 if (audio_get_number_of_active_tones() > 1) {
306 channel_2_set_frequency(audio_get_processed_frequency(1));
307 } else {
308 channel_2_stop();
309 }
310# endif
311 }
312}
313#endif
314
315#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
316ISR(AUDIO2_TIMERx_COMPy_vect) {
317 isr_counter++;
318 if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
319
320 isr_counter = 0;
321 bool state_changed = audio_update_state();
322
323 if (!playing_note && !playing_melody) {
324 channel_2_stop();
325 return;
326 }
327
328 if (state_changed) {
329 channel_2_set_frequency(audio_get_processed_frequency(0));
330 }
331}
332#endif
diff --git a/platforms/avr/drivers/i2c_master.c b/platforms/avr/drivers/i2c_master.c
index 2773e0077..111b55d6b 100644
--- a/platforms/avr/drivers/i2c_master.c
+++ b/platforms/avr/drivers/i2c_master.c
@@ -202,6 +202,25 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data,
202 return status; 202 return status;
203} 203}
204 204
205i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {
206 i2c_status_t status = i2c_start(devaddr | 0x00, timeout);
207 if (status >= 0) {
208 status = i2c_write(regaddr >> 8, timeout);
209
210 if (status >= 0) {
211 status = i2c_write(regaddr & 0xFF, timeout);
212
213 for (uint16_t i = 0; i < length && status >= 0; i++) {
214 status = i2c_write(data[i], timeout);
215 }
216 }
217 }
218
219 i2c_stop();
220
221 return status;
222}
223
205i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { 224i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
206 i2c_status_t status = i2c_start(devaddr, timeout); 225 i2c_status_t status = i2c_start(devaddr, timeout);
207 if (status < 0) { 226 if (status < 0) {
@@ -235,6 +254,43 @@ error:
235 return (status < 0) ? status : I2C_STATUS_SUCCESS; 254 return (status < 0) ? status : I2C_STATUS_SUCCESS;
236} 255}
237 256
257i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
258 i2c_status_t status = i2c_start(devaddr, timeout);
259 if (status < 0) {
260 goto error;
261 }
262
263 status = i2c_write(regaddr >> 8, timeout);
264 if (status < 0) {
265 goto error;
266 }
267 status = i2c_write(regaddr & 0xFF, timeout);
268 if (status < 0) {
269 goto error;
270 }
271
272 status = i2c_start(devaddr | 0x01, timeout);
273
274 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
275 status = i2c_read_ack(timeout);
276 if (status >= 0) {
277 data[i] = status;
278 }
279 }
280
281 if (status >= 0) {
282 status = i2c_read_nack(timeout);
283 if (status >= 0) {
284 data[(length - 1)] = status;
285 }
286 }
287
288error:
289 i2c_stop();
290
291 return (status < 0) ? status : I2C_STATUS_SUCCESS;
292}
293
238void i2c_stop(void) { 294void i2c_stop(void) {
239 // transmit STOP condition 295 // transmit STOP condition
240 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); 296 TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
diff --git a/platforms/avr/drivers/i2c_master.h b/platforms/avr/drivers/i2c_master.h
index e5af73364..2d95846db 100644
--- a/platforms/avr/drivers/i2c_master.h
+++ b/platforms/avr/drivers/i2c_master.h
@@ -39,5 +39,7 @@ int16_t i2c_read_nack(uint16_t timeout);
39i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); 39i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);
40i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); 40i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);
41i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); 41i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
42i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);
42i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); 43i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
44i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);
43void i2c_stop(void); 45void i2c_stop(void);
diff --git a/platforms/avr/drivers/ps2/ps2_io.c b/platforms/avr/drivers/ps2/ps2_io.c
new file mode 100644
index 000000000..7c826fbf1
--- /dev/null
+++ b/platforms/avr/drivers/ps2/ps2_io.c
@@ -0,0 +1,51 @@
1#include <stdbool.h>
2#include "ps2_io.h"
3#include "gpio.h"
4#include "wait.h"
5
6/* Check port settings for clock and data line */
7#if !(defined(PS2_CLOCK_PIN))
8# error "PS/2 clock setting is required in config.h"
9#endif
10
11#if !(defined(PS2_DATA_PIN))
12# error "PS/2 data setting is required in config.h"
13#endif
14
15/*
16 * Clock
17 */
18void clock_init(void) {}
19
20void clock_lo(void) {
21 // Transition from input with pull-up to output low via Hi-Z instead of output high
22 writePinLow(PS2_CLOCK_PIN);
23 setPinOutput(PS2_CLOCK_PIN);
24}
25
26void clock_hi(void) { setPinInputHigh(PS2_CLOCK_PIN); }
27
28bool clock_in(void) {
29 setPinInputHigh(PS2_CLOCK_PIN);
30 wait_us(1);
31 return readPin(PS2_CLOCK_PIN);
32}
33
34/*
35 * Data
36 */
37void data_init(void) {}
38
39void data_lo(void) {
40 // Transition from input with pull-up to output low via Hi-Z instead of output high
41 writePinLow(PS2_DATA_PIN);
42 setPinOutput(PS2_DATA_PIN);
43}
44
45void data_hi(void) { setPinInputHigh(PS2_DATA_PIN); }
46
47bool data_in(void) {
48 setPinInputHigh(PS2_DATA_PIN);
49 wait_us(1);
50 return readPin(PS2_DATA_PIN);
51}
diff --git a/platforms/avr/drivers/ps2/ps2_usart.c b/platforms/avr/drivers/ps2/ps2_usart.c
new file mode 100644
index 000000000..151cfcd68
--- /dev/null
+++ b/platforms/avr/drivers/ps2/ps2_usart.c
@@ -0,0 +1,227 @@
1/*
2Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
3
4This software is licensed with a Modified BSD License.
5All of this is supposed to be Free Software, Open Source, DFSG-free,
6GPL-compatible, and OK to use in both free and proprietary applications.
7Additions and corrections to this file are welcome.
8
9
10Redistribution and use in source and binary forms, with or without
11modification, are permitted provided that the following conditions are met:
12
13* Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16* Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21* Neither the name of the copyright holders nor the names of
22 contributors may be used to endorse or promote products derived
23 from this software without specific prior written permission.
24
25THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35POSSIBILITY OF SUCH DAMAGE.
36*/
37
38/*
39 * PS/2 protocol USART version
40 */
41
42#include <stdbool.h>
43#include <avr/interrupt.h>
44#include <util/delay.h>
45#include "gpio.h"
46#include "ps2.h"
47#include "ps2_io.h"
48#include "print.h"
49
50#ifndef PS2_CLOCK_DDR
51# define PS2_CLOCK_DDR PORTx_ADDRESS(PS2_CLOCK_PIN)
52#endif
53#ifndef PS2_CLOCK_BIT
54# define PS2_CLOCK_BIT (PS2_CLOCK_PIN & 0xF)
55#endif
56#ifndef PS2_DATA_DDR
57# define PS2_DATA_DDR PORTx_ADDRESS(PS2_DATA_PIN)
58#endif
59#ifndef PS2_DATA_BIT
60# define PS2_DATA_BIT (PS2_DATA_PIN & 0xF)
61#endif
62
63#define WAIT(stat, us, err) \
64 do { \
65 if (!wait_##stat(us)) { \
66 ps2_error = err; \
67 goto ERROR; \
68 } \
69 } while (0)
70
71uint8_t ps2_error = PS2_ERR_NONE;
72
73static inline uint8_t pbuf_dequeue(void);
74static inline void pbuf_enqueue(uint8_t data);
75static inline bool pbuf_has_data(void);
76static inline void pbuf_clear(void);
77
78void ps2_host_init(void) {
79 idle(); // without this many USART errors occur when cable is disconnected
80 PS2_USART_INIT();
81 PS2_USART_RX_INT_ON();
82 // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
83 //_delay_ms(2500);
84}
85
86uint8_t ps2_host_send(uint8_t data) {
87 bool parity = true;
88 ps2_error = PS2_ERR_NONE;
89
90 PS2_USART_OFF();
91
92 /* terminate a transmission if we have */
93 inhibit();
94 _delay_us(100); // [4]p.13
95
96 /* 'Request to Send' and Start bit */
97 data_lo();
98 clock_hi();
99 WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
100
101 /* Data bit[2-9] */
102 for (uint8_t i = 0; i < 8; i++) {
103 _delay_us(15);
104 if (data & (1 << i)) {
105 parity = !parity;
106 data_hi();
107 } else {
108 data_lo();
109 }
110 WAIT(clock_hi, 50, 2);
111 WAIT(clock_lo, 50, 3);
112 }
113
114 /* Parity bit */
115 _delay_us(15);
116 if (parity) {
117 data_hi();
118 } else {
119 data_lo();
120 }
121 WAIT(clock_hi, 50, 4);
122 WAIT(clock_lo, 50, 5);
123
124 /* Stop bit */
125 _delay_us(15);
126 data_hi();
127
128 /* Ack */
129 WAIT(data_lo, 50, 6);
130 WAIT(clock_lo, 50, 7);
131
132 /* wait for idle state */
133 WAIT(clock_hi, 50, 8);
134 WAIT(data_hi, 50, 9);
135
136 idle();
137 PS2_USART_INIT();
138 PS2_USART_RX_INT_ON();
139 return ps2_host_recv_response();
140ERROR:
141 idle();
142 PS2_USART_INIT();
143 PS2_USART_RX_INT_ON();
144 return 0;
145}
146
147uint8_t ps2_host_recv_response(void) {
148 // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
149 uint8_t retry = 25;
150 while (retry-- && !pbuf_has_data()) {
151 _delay_ms(1);
152 }
153 return pbuf_dequeue();
154}
155
156uint8_t ps2_host_recv(void) {
157 if (pbuf_has_data()) {
158 ps2_error = PS2_ERR_NONE;
159 return pbuf_dequeue();
160 } else {
161 ps2_error = PS2_ERR_NODATA;
162 return 0;
163 }
164}
165
166ISR(PS2_USART_RX_VECT) {
167 // TODO: request RESEND when error occurs?
168 uint8_t error = PS2_USART_ERROR; // USART error should be read before data
169 uint8_t data = PS2_USART_RX_DATA;
170 if (!error) {
171 pbuf_enqueue(data);
172 } else {
173 xprintf("PS2 USART error: %02X data: %02X\n", error, data);
174 }
175}
176
177/* send LED state to keyboard */
178void ps2_host_set_led(uint8_t led) {
179 ps2_host_send(0xED);
180 ps2_host_send(led);
181}
182
183/*--------------------------------------------------------------------
184 * Ring buffer to store scan codes from keyboard
185 *------------------------------------------------------------------*/
186#define PBUF_SIZE 32
187static uint8_t pbuf[PBUF_SIZE];
188static uint8_t pbuf_head = 0;
189static uint8_t pbuf_tail = 0;
190static inline void pbuf_enqueue(uint8_t data) {
191 uint8_t sreg = SREG;
192 cli();
193 uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
194 if (next != pbuf_tail) {
195 pbuf[pbuf_head] = data;
196 pbuf_head = next;
197 } else {
198 print("pbuf: full\n");
199 }
200 SREG = sreg;
201}
202static inline uint8_t pbuf_dequeue(void) {
203 uint8_t val = 0;
204
205 uint8_t sreg = SREG;
206 cli();
207 if (pbuf_head != pbuf_tail) {
208 val = pbuf[pbuf_tail];
209 pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
210 }
211 SREG = sreg;
212
213 return val;
214}
215static inline bool pbuf_has_data(void) {
216 uint8_t sreg = SREG;
217 cli();
218 bool has_data = (pbuf_head != pbuf_tail);
219 SREG = sreg;
220 return has_data;
221}
222static inline void pbuf_clear(void) {
223 uint8_t sreg = SREG;
224 cli();
225 pbuf_head = pbuf_tail = 0;
226 SREG = sreg;
227}