reform

MNT Reform: Open Source Portable Computer
Log (Feed) | Files | Refs (Tags) | README

commit f87162348f806d3c3a347029af4bece19f8421e9
parent e63751523b13d6c48a3bcc0b4ca338497641dea0
Author: mntmn <lukas@mntmn.com>
Date:   Thu, 28 Nov 2019 21:01:34 +0100

Merge branch 'reform2-nitrogen8m' of https://source.mntmn.com/MNT/reform into reform2-nitrogen8m

Diffstat:
Mreform-keyboard2-fw/Keyboard.c | 20+++++++++++++++-----
Mreform-keyboard2-fw/Makefile | 2+-
Areform-keyboard2-fw/serial.c | 454+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Areform-keyboard2-fw/serial.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mreform-keyboard2-fw/ssd1306.c | 4++++
Mreform-keyboard2-fw/ssd1306.h | 1+
6 files changed, 543 insertions(+), 6 deletions(-)

diff --git a/reform-keyboard2-fw/Keyboard.c b/reform-keyboard2-fw/Keyboard.c @@ -94,7 +94,7 @@ void SetupHardware() // declare port pins as inputs (0) and outputs (1) DDRB = 0b11110000; - DDRD = 0b11010011; + DDRD = 0b11010000; DDRF = 0b10000000; DDRE = 0b00000000; DDRC = 0b00000000; @@ -110,7 +110,14 @@ void SetupHardware() MCUCR |=(1<<JTD); iota_gfx_init(true); - iota_gfx_write("Hello."); + + gfx_poke(0,0,'R'); + gfx_poke(1,0,'e'); + gfx_poke(2,0,'f'); + gfx_poke(3,0,'o'); + gfx_poke(4,0,'r'); + gfx_poke(5,0,'m'); + iota_gfx_flush(); ser_init(&PORTE, 6, &PORTB, 7, false); ser_begin(115200); @@ -330,6 +337,7 @@ void remote_turn_off_som(void) { } char metaPressed = 0; +uint8_t lastKeyCodes = 0; bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, uint8_t* const ReportID, @@ -408,9 +416,11 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn metaPressed = metaPressedNow; - if (keyPressedNow) { - //iota_gfx_write_char('!'); - ser_write('!'); + if (lastKeyCodes!=usedKeyCodes) { + gfx_poke(0,2,usedKeyCodes+'0'); + iota_gfx_flush(); + ser_write(usedKeyCodes+'0'); + lastKeyCodes = usedKeyCodes; } *ReportSize = sizeof(USB_KeyboardReport_Data_t); diff --git a/reform-keyboard2-fw/Makefile b/reform-keyboard2-fw/Makefile @@ -18,7 +18,7 @@ F_CPU = 16000000 F_USB = $(F_CPU) OPTIMIZATION = s TARGET = Keyboard -SRC = $(TARGET).c Descriptors.c i2c.c ssd1306.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) +SRC = $(TARGET).c Descriptors.c i2c.c ssd1306.c serial.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) LUFA_PATH = ./lufa-master/LUFA CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER $(REFORM_KBD_OPTIONS) -IConfig/ LD_FLAGS = diff --git a/reform-keyboard2-fw/serial.c b/reform-keyboard2-fw/serial.c @@ -0,0 +1,454 @@ +/* +NewSoftSerial.cpp +Multi-instance software serial library for Arduino/Wiring +-- Interrupt-driven receive and other improvements by ladyada + (http://ladyada.net) +-- Tuning, circular buffer, derivation from class Print/Stream, + multi-instance support, porting to 8MHz processors, + various optimizations, PROGMEM delay tables, inverse logic and + direct port writing by Mikal Hart (http://www.arduiniana.org) +-- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) +-- 20MHz processor support by Garrett Mace (http://www.macetech.com) +-- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +The latest version of this library can always be found at +http://arduiniana.org. +*/ + +#include <avr/interrupt.h> +#include <avr/pgmspace.h> +#include <avr/io.h> +#include <stdlib.h> +#include <stdbool.h> + +//#include "WConstants.h" +//#include "pins_arduino.h" +#include "serial.h" +// +// Lookup table +// +typedef struct _DELAY_TABLE +{ + long baud; + unsigned short rx_delay_centering; + unsigned short rx_delay_intrabit; + unsigned short rx_delay_stopbit; + unsigned short tx_delay; +} DELAY_TABLE; + +#if F_CPU == 16000000 + +static const DELAY_TABLE PROGMEM table[] = +{ + // baud rxcenter rxintra rxstop tx + { 115200, 1, 17, 17, 12, }, + { 57600, 10, 37, 37, 33, }, + { 38400, 25, 57, 57, 54, }, + { 31250, 31, 70, 70, 68, }, + { 28800, 34, 77, 77, 74, }, + { 19200, 54, 117, 117, 114, }, + { 14400, 74, 156, 156, 153, }, + { 9600, 114, 236, 236, 233, }, + { 4800, 233, 474, 474, 471, }, + { 2400, 471, 950, 950, 947, }, + { 1200, 947, 1902, 1902, 1899, }, + { 300, 3804, 7617, 7617, 7614, }, +}; + +const int XMIT_START_ADJUSTMENT = 5; + +#elif F_CPU == 8000000 + +static const DELAY_TABLE table[] PROGMEM = +{ + // baud rxcenter rxintra rxstop tx + { 115200, 1, 5, 5, 3, }, + { 57600, 1, 15, 15, 13, }, + { 38400, 2, 25, 26, 23, }, + { 31250, 7, 32, 33, 29, }, + { 28800, 11, 35, 35, 32, }, + { 19200, 20, 55, 55, 52, }, + { 14400, 30, 75, 75, 72, }, + { 9600, 50, 114, 114, 112, }, + { 4800, 110, 233, 233, 230, }, + { 2400, 229, 472, 472, 469, }, + { 1200, 467, 948, 948, 945, }, + { 300, 1895, 3805, 3805, 3802, }, +}; + +const int XMIT_START_ADJUSTMENT = 4; + +#error This version of NewSoftSerial supports only 16 and 8MHz processors + +#endif + +#define HIGH 1 +#define LOW 0 + +// per object data +uint8_t _receivePin; +uint8_t _receiveBitMask; +volatile uint8_t *_receivePortRegister; +uint8_t _transmitBitMask; +volatile uint8_t *_transmitPortRegister; + +uint16_t _rx_delay_centering; +uint16_t _rx_delay_intrabit; +uint16_t _rx_delay_stopbit; +uint16_t _tx_delay; + +uint16_t _buffer_overflow=1; +uint16_t _inverse_logic=1; + +// static data +static char _receive_buffer[_SS_MAX_RX_BUFF]; +static volatile uint8_t _receive_buffer_tail; +static volatile uint8_t _receive_buffer_head; + +// public only for easy access by interrupt handlers +static inline void ser_handle_interrupt(void); + +// +// Private methods +// + +/* static */ +inline void ser_tunedDelay(uint16_t delay) { + uint8_t tmp=0; + register uint16_t dly asm("r24") = delay; + + asm volatile("sbiw %0, 0x01 \n\t" + "ldi %1, 0xFF \n\t" + "cpi %A0, 0xFF \n\t" + "cpc %B0, %1 \n\t" + "brne .-10 \n\t" + : "+r" (dly), "+a" (tmp) + : "0" (dly) + ); +} + +bool _listening = false; + +bool ser_listen() +{ + _buffer_overflow = false; + uint8_t oldSREG = SREG; + cli(); + _receive_buffer_head = _receive_buffer_tail = 0; + SREG = oldSREG; + + _listening = true; + + return true; +} + +// +// The receive routine called by the interrupt handler +// +void ser_recv() +{ + uint8_t d = 0; + + // If RX line is high, then we don't see any start bit + // so interrupt is probably not for us + if (_inverse_logic ? ser_rx_pin_read() : !ser_rx_pin_read()) + { + // Wait approximately 1/2 of a bit width to "center" the sample + ser_tunedDelay(_rx_delay_centering); + + // Read each of the 8 bits + for (uint8_t i=0x1; i; i <<= 1) + { + ser_tunedDelay(_rx_delay_intrabit); + uint8_t noti = ~i; + if (ser_rx_pin_read()) + d |= i; + else // else clause added to ensure function timing is ~balanced + d &= noti; + } + + // skip the stop bit + ser_tunedDelay(_rx_delay_stopbit); + + if (_inverse_logic) + d = ~d; + + // if buffer full, set the overflow flag and return + if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + _receive_buffer[_receive_buffer_tail] = d; // save new byte + _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + } + else + { + _buffer_overflow = true; + } + } +} + +void ser_tx_pin_write(uint8_t pin_state) +{ + if (pin_state == LOW) + *_transmitPortRegister &= ~_transmitBitMask; + else + *_transmitPortRegister |= _transmitBitMask; +} + +uint8_t ser_rx_pin_read() +{ + return *_receivePortRegister & _receiveBitMask; +} + +// +// Interrupt handling +// + +static inline void ser_handle_interrupt() +{ + ser_recv(); +} + +#if defined(PCINT0_vect) +ISR(PCINT0_vect) +{ + ser_handle_interrupt(); +} +#endif + +#if defined(PCINT1_vect) +ISR(PCINT1_vect) +{ + ser_handle_interrupt(); +} +#endif + +#if defined(PCINT2_vect) +ISR(PCINT2_vect) +{ + ser_handle_interrupt(); +} +#endif + +#if defined(PCINT3_vect) +ISR(PCINT3_vect) +{ + ser_handle_interrupt(); +} +#endif + +// +// Constructor +// +void ser_init(volatile uint8_t* rx_port, uint8_t rx_pin, volatile uint8_t* tx_port, uint8_t tx_pin, bool inverse_logic) +{ + _rx_delay_centering = 0; + _rx_delay_intrabit = 0; + _rx_delay_stopbit = 0; + _tx_delay = 0; + _buffer_overflow = false; + _inverse_logic = inverse_logic; + ser_set_tx(rx_port, rx_pin); + ser_set_rx(rx_port, rx_pin); +} + +bool ser_is_listening() { + return _listening; +} + +bool ser_overflow() { + bool ret = _buffer_overflow; + _buffer_overflow = false; + return ret; +} + +#define output_low(port,pin) *port &= ~(1<<pin) +#define output_high(port,pin) *port |= (1<<pin) + +void ser_set_tx(volatile uint8_t* port, uint8_t pin) +{ + output_high(port, pin); + //digitalWrite(tx, HIGH); + _transmitBitMask = (1<<pin); + _transmitPortRegister = port; +} + +void ser_set_rx(volatile uint8_t* port, uint8_t pin) +{ + //if (!_inverse_logic) + // digitalWrite(rx, HIGH); // pullup for normal logic! + //_receivePin = rx; + _receiveBitMask = (1<<pin); + _receivePortRegister = port; +} + +// +// Public methods +// + +void ser_begin(long speed) +{ + _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; + + for (unsigned i=0; i<sizeof(table)/sizeof(table[0]); ++i) + { + long baud = pgm_read_dword(&table[i].baud); + if (baud == speed) + { + _rx_delay_centering = pgm_read_word(&table[i].rx_delay_centering); + _rx_delay_intrabit = pgm_read_word(&table[i].rx_delay_intrabit); + _rx_delay_stopbit = pgm_read_word(&table[i].rx_delay_stopbit); + _tx_delay = pgm_read_word(&table[i].tx_delay); + break; + } + } + + // Set up RX interrupts, but only if we have a valid RX baud rate + if (_rx_delay_stopbit) + { + //PCICR |= 1; // enable pin change interrupt + + EICRB = (1<<4); // ISC60: Any logical change on INT6 generates an interrupt request + EIMSK |= (1<<6); // INT6 unmask + + ser_tunedDelay(_tx_delay); // if we were low this establishes the end + } + + ser_listen(); +} + +void ser_end() +{ + // TODO disable interrupts + EICRB = 0; +} + + +// Read data from buffer +int ser_read() +{ + if (!ser_is_listening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte + _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; + return d; +} + +int ser_available() +{ + if (!ser_is_listening()) + return 0; + + return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF; +} + +void ser_write(uint8_t b) +{ + if (_tx_delay == 0) + return; + + uint8_t oldSREG = SREG; + cli(); // turn off interrupts for a clean txmit + + // Write the start bit + ser_tx_pin_write(_inverse_logic ? HIGH : LOW); + ser_tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT); + + // Write each of the 8 bits + if (_inverse_logic) + { + for (uint8_t mask = 0x01; mask; mask <<= 1) + { + if (b & mask) // choose bit + ser_tx_pin_write(LOW); // send 1 + else + ser_tx_pin_write(HIGH); // send 0 + + ser_tunedDelay(_tx_delay); + } + + ser_tx_pin_write(LOW); // restore pin to natural state + } + else + { + for (uint8_t mask = 0x01; mask; mask <<= 1) + { + if (b & mask) // choose bit + ser_tx_pin_write(HIGH); // send 1 + else + ser_tx_pin_write(LOW); // send 0 + + ser_tunedDelay(_tx_delay); + } + + ser_tx_pin_write(HIGH); // restore pin to natural state + } + + SREG = oldSREG; // turn interrupts back on + ser_tunedDelay(_tx_delay); +} + +#if !defined(cbi) +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +void ser_enable_timer0(bool enable) +{ + if (enable) +#if defined(__AVR_ATmega8__) + sbi(TIMSK, TOIE0); +#else + sbi(TIMSK0, TOIE0); +#endif + else +#if defined(__AVR_ATmega8__) + cbi(TIMSK, TOIE0); +#else + cbi(TIMSK0, TOIE0); +#endif +} + +void ser_flush() +{ + if (!ser_is_listening()) + return; + + uint8_t oldSREG = SREG; + cli(); + _receive_buffer_head = _receive_buffer_tail = 0; + SREG = oldSREG; +} + +int ser_peek() +{ + if (!ser_is_listening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + return _receive_buffer[_receive_buffer_head]; +} diff --git a/reform-keyboard2-fw/serial.h b/reform-keyboard2-fw/serial.h @@ -0,0 +1,68 @@ +/* +NewSoftSerial.h +Multi-instance software serial library for Arduino/Wiring +-- Interrupt-driven receive and other improvements by ladyada + (http://ladyada.net) +-- Tuning, circular buffer, derivation from class Print/Stream, + multi-instance support, porting to 8MHz processors, + various optimizations, PROGMEM delay tables, inverse logic and + direct port writing by Mikal Hart (http://www.arduiniana.org) +-- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) +-- 20MHz processor support by Garrett Mace (http://www.macetech.com) +-- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +The latest version of this library can always be found at +http://arduiniana.org. +*/ + +#ifndef NewSoftSerial_h +#define NewSoftSerial_h + +/****************************************************************************** +* Definitions +******************************************************************************/ + +#define _SS_MAX_RX_BUFF 64 // RX buffer size +#define _SS_VERSION 12 // software version of this library +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + + +// private methods +void ser_recv(void); +uint8_t ser_rx_pin_read(void); +void ser_tx_pin_write(uint8_t pin_state); +void ser_set_tx(volatile uint8_t* port, uint8_t pin); +void ser_set_rx(volatile uint8_t* port, uint8_t pin); + +// public methods +void ser_init(volatile uint8_t* rx_port, uint8_t rx_pin, volatile uint8_t* tx_port, uint8_t tx_pin, bool inverse_logic); +void ser_begin(long speed); +bool ser_listen(void); +void ser_end(void); +bool ser_is_listening(void); +bool ser_overflow(void); +void ser_enable_timer0(bool enable); +int ser_peek(void); + +void ser_write(uint8_t byte); +int ser_read(void); +int ser_available(void); +void ser_flush(void); + +#endif diff --git a/reform-keyboard2-fw/ssd1306.c b/reform-keyboard2-fw/ssd1306.c @@ -417,6 +417,10 @@ void matrix_write_char(struct CharacterMatrix *matrix, uint8_t c) { matrix_write_char_inner(matrix, c); } +void gfx_poke(uint8_t x, uint8_t y, uint8_t c) { + display.display[y][x] = c; +} + void iota_gfx_write_char(uint8_t c) { matrix_write_char(&display, c); } diff --git a/reform-keyboard2-fw/ssd1306.h b/reform-keyboard2-fw/ssd1306.h @@ -67,6 +67,7 @@ struct CharacterMatrix { struct CharacterMatrix display; +void gfx_poke(uint8_t x, uint8_t y, uint8_t c); bool iota_gfx_init(bool rotate); void iota_gfx_task(void); bool iota_gfx_off(void);