*lots* of stuff .. :) - got PWM working!

also, i didn't make a note of it in the *.md file, but Waveform
Generation Mode 15 for fast PWM wasn't working right (well.. wasn't
working how i expected it to).  i misinterpreted what the modes were
doing, partially (haha, or all-ly?) because i didn't read the
description of fast pwm thoroughly enough...  in any case, all the
information's in the datasheet, and it's actually not terribly long.
i'm not sure how to correctly use Mode 15 yet, but i think i'll leave it
alone for now, since Mode 5 works as expected, and i think what the
datasheet says about *that* makes enough sense to me for me to be
content for the moment.
partial-rewrite
Ben Blazak 2012-03-19 00:01:41 -07:00
parent 4dd05f515b
commit 19725eed4a
24 changed files with 543 additions and 404 deletions

View File

@ -125,6 +125,14 @@
(https://github.com/arduino/Arduino/tree/master/libraries/Wire/utility)
: (arduino / Arduino / libraries / Wire / utility)
* forum: [Interfacing MCP23018 io expander via Arduino]
(http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1282150189)
: contains a quick MCP23017 library in C++
* github: [maniacbug / Arduino / libraries / MCP23018]
(https://github.com/maniacbug/Arduino/tree/master/libraries/MCP23018)
: library in C++
* zip: [PJRC: usb_keyboard]
(http://pjrc.com/teensy/usb_keyboard.zip)
@ -182,3 +190,9 @@
* from [the PJRC website]
(http://pjrc.com/teensy/datasheets.html)
-------------------------------------------------------------------------------
Copyright © 2012 Ben Blazak
Released under The MIT License (MIT) (see "license.md") at
<https://github.com/benblazak/ergodox-firmware>

View File

@ -3,3 +3,9 @@
[ergodox-firmware]: https://github.com/benblazak/ergodox-firmware
[ergodox keyboard]: http://geekhack.org/showthread.php?22780-Interest-Check-Custom-split-ergo-keyboard
-------------------------------------------------------------------------------
Copyright &copy; 2012 Ben Blazak
Released under The MIT License (MIT) (see "license.md") at
<https://github.com/benblazak/ergodox-firmware>

View File

@ -1,235 +0,0 @@
/* ----------------------------------------------------------------------------
* ergodox controller (Teensy 2.0 and MCP23018) specific stuff
* - public things are prefixed by `controller_` or `CONTROLLER_`
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~ documentation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* ----------------------------------------------------------------------------
* Pinouts and Pin assignments
* - '+' indicates pin
* - 'o' indicates unused pin
* - '-'s inserted between some of the pin functions for readability
* - 'OC**' pins enclosed in parenthesis had lines over them in the pinout
* ----------------------------------------------------------------------------
* ----------------------------------------------------------------------------
* Teensy 2.0
* ========== GND +---.....---+ VCC
* SS PB0 + + PF0 ADC0
* SCLK PB1 + + PF1 ADC1
* MOSI PB2 + + PF4 ADC4
* MISO PB3 + + + + PF5 ADC5
* RTS OC1C OC0A --------- PB7 + PE6 AREF + PF6 ADC6
* OC0B INT0 SCL PD0 + AIN0 + PF7 ADC7
* INT1 SDA PD1 + INT6 + PB6 ADC13 OC1B OC4B
* RXD1 ----------- INT2 --- PD2 + + PB5 ADC12 OC1A (OC4B)
* TXD1 ----------- INT3 --- PD3 + + PB4 ADC11
* OC3A (OC4A) -------- PC6 + + PD7 ADC10 T0 -- OC4D
* ICP3 ----- OC4A --------- PC7 +-+-+-+-+-+-+ PD6 ADC9 T1 - (OC4D) onboardLED
* CTS XCK1 PD5 --/ | | | \-- PD4 ADC8 ------------ ICP1
* VCC ------------------/ | \-------------- RST
* GND --------------------/
* ----------------------------------------------------------------------------
* MCP23018
* ======== Vss(GND) +01---.---28+ NC
* NC +02 27+ GPA7
* GPB0 +03 26+ GPA6
* GPB1 +04 25+ GPA5
* GPB2 +05 24+ GPA4
* GPB3 +06 23+ GPA3
* GPB4 +07 22+ GPA2
* GPB5 +08 21+ GPA1
* GPB6 +09 20+ GPA0
* GPB7 +10 19+ INTA
* Vdd(Vcc) +11 18+ INTB
* SCL +12 17+ NC
* SDA +13 16+ RESET
* NC +14-------15+ ADDR
* ----------------------------------------------------------------------------
* ----------------------------------------------------------------------------
* Teensy 2.0 Pin Assignments
* ==========================
* power_negative GND +---.....---+ Vcc power_positive
* column6 PB0 + + PF0 row6
* o + PF1 row7
* o + PF4 row8
* o o o + PF5 row9
* LED3 OC1C + + PF6 rowA
* I2C SCL + + PF7 rowB
* I2C SDA + + OC1B LED2
* column3 PD2 + + OC1A LED1
* column4 PD3 + + PB4 column0
* column1 PC6 + + PD7 column5
* column2 PC7 +-o-o-o-o-o-o
*
* notes:
* - SCL and SDA: Need external pull-up resistors. Sometimes the internal
* pull-ups are enough (see datasheet section 20.5.1), but i think for this
* project we'll want external ones, in case people want to separate the
* halves very far.
* ----------------------------------------------------------------------------
* MCP32018 Pin Assignments
* ========================
* power_negative Vss(GND) +01---.---28o
* o02 27o
* column0 GPB0 +03 26o
* column1 GPB1 +04 25+ GPA5 rowB
* column2 GPB2 +05 24+ GPA4 rowA
* column3 GPB3 +06 23+ GPA3 row9
* column4 GPB4 +07 22+ GPA2 row8
* column5 GPB5 +08 21+ GPA1 row7
* column6 GPB6 +09 20+ GPA0 row6
* o10 19o
* power_positive Vdd(Vcc) +11 18o
* I2C SCL +12 17o
* I2C SDA +13 16o
* o14-------15+ ADDR (see note)
*
* notes:
* - ADDR (pin15): Set slave address to 0b0100000 by connecting to Vss(GND).
* (The user-defined bits are the three least significant).
* ------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
* Notes about Registers
* ----------------------------------------------------------------------------
* General I/O (see datasheet section 10.2.1)
*
* DDRxn function PINxn function
* 1 output 1 drive high
* 0 drive low
* 0 input 1 internal pull-up on
* 0 internal pull-up off
*
* notes:
* - Unused pins should be set as input with internal pullup enabled (see
* datasheet section 10.2.6).
* ----------------------------------------------------------------------------
* PWM on ports OC1(A|B|C) (see datasheet section 14.10)
*
* notes:
* - PWM pins should be set as outputs.
* - we want Waveform Generation Mode 15
* (fast PWM, TOP = OCRnA)
* (see table 14-5)
* - set (TCCRB[4,3],TCCRA[1,0]) to (1,1,1,1)
* - we want "Compare Output Mode, Fast PWM" to be 0b10
* "Clear OCnA/OCnB/OCnC on compare match, set OCnA/OCnB/OCnC at TOP"
* (see table 14-3)
* this way higher values of OCR1(A|B|C) will mean longer 'on' times for the
* LEDs
* - when in a fast PWM mode, set (TCCR1A[7,6,5,4,3,2]) to (1,0,1,0,1,0)
* - we want "Clock Select Bit Description" to be 0b001
* "clkI/O/1 (No prescaling)"
* (see table 14-6)
* - set (TCCR1B[2,1,0]) to (0,0,1)
* - LEDs will be at minimum brightness until OCR1(A|B|C) are changed (since
* the default value of all the bits in those registers is 0)
*
* abbreviations:
* - OCR = Output Compare Register
* - TCCR = Timer/Counter Control Register
* ------------------------------------------------------------------------- */
#include "lib/twi.h"
#include "teensy-2-0--mcp23018.h"
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~ macros ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// processor frequency (from <http://www.pjrc.com/teensy/prescaler.html>)
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
#define CPU_16MHz 0x00
#define CPU_8MHz 0x01
#define CPU_4MHz 0x02
#define CPU_2MHz 0x03
#define CPU_1MHz 0x04
#define CPU_500kHz 0x05
#define CPU_250kHz 0x06
#define CPU_125kHz 0x07
#define CPU_62kHz 0x08
// TWI frequency
#define TWI_FREQ 400000 // (see lib/twi.(h|c))
// pins
// --- rows ?TODO
// --- columns ?TODO
// mcp23018 pins
// TODO
// I2C SCL
// I2C SDA
// ROW_6 GPA0
// ROW_7 GPA1
// ROW_8 GPA2
// ROW_9 GPA3
// ROW_A GPA4
// ROW_B GPA5
// COLUMN_0_LH GPB0
// COLUMN_1_LH GPB1
// COLUMN_2_LH GPB2
// COLUMN_3_LH GPB3
// COLUMN_4_LH GPB4
// COLUMN_5_LH GPB5
// COLUMN_6_LH GPB6
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~ functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void controller_init() {
teensy_init(); // must be first
mcp23018_init(); // must be second
}
void teensy_init() {
CPU_PRESCALE(CPU_16MHz); // speed should match F_CPU in makefile
// unused pins
DDRB &= ~0b00001110; // set B(1,2,3) as input
PORTB |= 0b00001110; // set B(1,2,3) internal pull-up enabled
DDRD &= ~0b01110000; // set D(4,5,6) as input
PORTD |= 0b01110000; // set D(4,5,6) internal pull-up enabled
DDRE &= ~0b01000000; // set E(6) as input
PORTE |= 0b01000000; // set E(6) internal pull-up enabled
// LEDs
DDRB |= 0b11100000; // set B(5,6,7) as output
TCCR1A = 0b10101011; // set and configure fast PWM
TCCR1B = 0b00011001; // set and configure fast PWM
// rows
DDRF |= 0b11110011; // set F(0,1,4,5,6,7) as output
PORTF |= 0b11110011; // set F(0,1,4,5,6,7) drive high
// columns
DDRB &= ~0b00010001; // set B(0,4) as input
PORTB |= 0b00010001; // set B(0,4) internal pull-up enabled
DDRC &= ~0b11000000; // set C(6,7) as input
PORTC |= 0b11000000; // set C(6,7) internal pull-up enabled
DDRD &= ~0b10001100; // set D(2,3,7) as input
PORTD |= 0b10001100; // set D(2,3,7) internal pull-up enabled
// I2C (TWI)
twi_init(); // (on pins D(0,1))
twi_setAddress(0b0100000);
}
// TODO
void mcp23018_init() {
}

View File

@ -1,28 +0,0 @@
/* ----------------------------------------------------------------------------
* ergodox controller (Teensy 2.0 and MCP23018) specific stuff
* - public things are prefixed by `controller_` or `CONTROLLER_`
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
// LEDs
#define CONTROLLER_LED1_ON (OCR1A = 0xFFFF)
#define CONTROLLER_LED1_OFF (OCR1A = 0x0000)
#define CONTROLLER_LED1_SET(n) (OCR1A = (uint16_t)(n))
#define CONTROLLER_LED1_SET_PERCENT(n) (OCR1A = (uint16_t)((n) * 0xFFFF))
#define CONTROLLER_LED2_ON (OCR1B = 0xFFFF)
#define CONTROLLER_LED2_OFF (OCR1B = 0x0000)
#define CONTROLLER_LED2_SET(n) (OCR1B = (uint16_t)(n))
#define CONTROLLER_LED2_SET_PERCENT(n) (OCR1B = (uint16_t)((n) * 0xFFFF))
#define CONTROLLER_LED3_ON (OCR1C = 0xFFFF)
#define CONTROLLER_LED3_OFF (OCR1C = 0x0000)
#define CONTROLLER_LED3_SET(n) (OCR1C = (uint16_t)(n))
#define CONTROLLER_LED3_SET_PERCENT(n) (OCR1C = (uint16_t)((n) * 0xFFFF))
// init
void controller_init();

View File

@ -1,5 +1,5 @@
/* ----------------------------------------------------------------------------
* Firmware for the ergoDOX keyboard
* Firmware main file
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
@ -7,13 +7,13 @@
* ------------------------------------------------------------------------- */
#include "lib/print.h"
#include "lib/pjrc/print.h"
#include "keyboard.h"
void main() {
init();
}
void init() {
}
// TODO
// int main(void) {
// return 0;
// }

12
src/keyboard.h Normal file
View File

@ -0,0 +1,12 @@
/* ----------------------------------------------------------------------------
* Keyboard specific stuff
* - public things are prefixed by `kb_` or `KB_`
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
// TODO

0
src/keyboard/ergodox.h Normal file
View File

View File

@ -36,67 +36,47 @@
#include "layout.h"
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~ macros ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// TODO
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~ variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
uint8_t kb_is_pressed[KB_ROWS][KB_COLUMNS] = {
// uint8_t kb_is_pressed[KB_ROWS][KB_COLUMNS] = {
// --- right hand ---
// column 0 1 2 3 4 5 6
0, 0, 0, 0, 0, 0, 0, //row 0x0
0, 0, 0, 0, 0, 0, 0, //row 0x1
0, 0, 0, 0, 0, 0, 0, //row 0x2
0, 0, 0, 0, 0, 0, 0, //row 0x3
0, 0, 0, 0, 0, 0, 0, //row 0x4
0, 0, 0, 0, 0, 0, 0, //row 0x5
// 0, 0, 0, 0, 0, 0, 0, //row 0x0
// 0, 0, 0, 0, 0, 0, 0, //row 0x1
// 0, 0, 0, 0, 0, 0, 0, //row 0x2
// 0, 0, 0, 0, 0, 0, 0, //row 0x3
// 0, 0, 0, 0, 0, 0, 0, //row 0x4
// 0, 0, 0, 0, 0, 0, 0, //row 0x5
// --- left hand ---
// column 0 1 2 3 4 5 6
0, 0, 0, 0, 0, 0, 0, //row 0x6
0, 0, 0, 0, 0, 0, 0, //row 0x7
0, 0, 0, 0, 0, 0, 0, //row 0x8
0, 0, 0, 0, 0, 0, 0, //row 0x9
0, 0, 0, 0, 0, 0, 0, //row 0xA
0, 0, 0, 0, 0, 0, 0, //row 0xB
}
// 0, 0, 0, 0, 0, 0, 0, //row 0x6
// 0, 0, 0, 0, 0, 0, 0, //row 0x7
// 0, 0, 0, 0, 0, 0, 0, //row 0x8
// 0, 0, 0, 0, 0, 0, 0, //row 0x9
// 0, 0, 0, 0, 0, 0, 0, //row 0xA
// 0, 0, 0, 0, 0, 0, 0 //row 0xB
// };
//
// TODO: this belongs in program space
uint8_t kb_maps[KB_LAYERS][KB_ROWS][KB_COLUMNS] = {
// uint8_t layout_layers[LAYOUT_LAYERS][LAYOUT_ROWS][LAYOUT_COLUMNS] = {
// ------- layer: default -------
// --- right hand ---
// column 0 1 2 3 4 5 6
0, 0, 0, 0, 0, 0, 0, //row 0x0
0, 0, 0, 0, 0, 0, 0, //row 0x1
0, 0, 0, 0, 0, 0, 0, //row 0x2
0, 0, 0, 0, 0, 0, 0, //row 0x3
0, 0, 0, 0, 0, 0, 0, //row 0x4
0, 0, 0, 0, 0, 0, 0, //row 0x5
// 0, 0, 0, 0, 0, 0, 0, //row 0x0
// 0, 0, 0, 0, 0, 0, 0, //row 0x1
// 0, 0, 0, 0, 0, 0, 0, //row 0x2
// 0, 0, 0, 0, 0, 0, 0, //row 0x3
// 0, 0, 0, 0, 0, 0, 0, //row 0x4
// 0, 0, 0, 0, 0, 0, 0, //row 0x5
// --- left hand ---
// column 0 1 2 3 4 5 6
0, 0, 0, 0, 0, 0, 0, //row 0x6
0, 0, 0, 0, 0, 0, 0, //row 0x7
0, 0, 0, 0, 0, 0, 0, //row 0x8
0, 0, 0, 0, 0, 0, 0, //row 0x9
0, 0, 0, 0, 0, 0, 0, //row 0xA
0, 0, 0, 0, 0, 0, 0, //row 0xB
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~ functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// TODO
void kb_init() {
}
// TODO
// - cycle through row=HIGH and read each column
void kb_update() {
}
// 0, 0, 0, 0, 0, 0, 0, //row 0x6
// 0, 0, 0, 0, 0, 0, 0, //row 0x7
// 0, 0, 0, 0, 0, 0, 0, //row 0x8
// 0, 0, 0, 0, 0, 0, 0, //row 0x9
// 0, 0, 0, 0, 0, 0, 0, //row 0xA
// 0, 0, 0, 0, 0, 0, 0 //row 0xB
// };

View File

@ -1,6 +1,5 @@
/* ----------------------------------------------------------------------------
* ergoDOX layout specific stuff
* - public things are prefixed by `layout_` or `LAYOUT_`
* see "default.c"
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
@ -8,9 +7,9 @@
* ------------------------------------------------------------------------- */
#define KB_LAYERS 1
#define KB_ROWS 12 // must match real life
#define KB_COLUMNS 7 // must match real life
#define LAYOUT_LAYERS 1
#define LAYOUT_ROWS 12 // must match real life
#define LAYOUT_COLUMNS 7 // must match real life
// TODO

View File

@ -0,0 +1,25 @@
#include <util/delay.h>
#include "teensy-2-0.h"
int main(void) {
teensy_init();
TEENSY_LED1_ON;
TEENSY_LED2_ON;
TEENSY_LED3_ON;
for(uint8_t i=0;;i++) {
TEENSY_LED1_SET( (i+(3*0xFF/2/3)) % 0xFF/2 );
TEENSY_LED2_SET( (i+(2*0xFF/2/3)) % 0xFF/2 );
TEENSY_LED3_SET( (i+(1*0xFF/2/3)) % 0xFF/2 );
// counting_up
// ? (i == 0xFF ? counting_up = 0 : i++)
// : (i == 0x00 ? counting_up = 1 : i--);
_delay_ms(10);
}
return 0;
}

View File

@ -0,0 +1,61 @@
/* ----------------------------------------------------------------------------
* ergoDOX controller: MCP23018 specific code
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
#define TWI_ADDRESS MCP23018_TWI_ADDRESS // from header file
#define True ((uint8_t)1)
#define False ((uint8_t)0)
// register addresses (see "mcp23018.md")
#define IODIRA 0x00 // i/o direction register
#define IODIRB 0x01
#define GPPUA 0x0C // GPIO pull-up resistor register
#define GPPUB 0x0D
#define GPIOA 0x12 // general purpose i/o port register
#define GPIOB 0x13
#define OLATA 0x14 // output latch register
#define OLATB 0x15
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "lib/arduino/twi.h"
#include "mcp23018.h"
void mcp23018_init(void) {
uint8_t data[3];
// set pin direction
// - unused : input : 1
// - rows : output : 0
// - columns : input : 1
data[0] = IODIRA; // start register address
data[1] = 0b11000000; // IODIRA
data[2] = 0b11111111; // IODIRB
twi_writeTo(TWI_ADDRESS, data, 3, True);
// set pull-up
// - unused : on : 1
// - rows : off : 0
// - columns : on : 1
data[0] = GPPUA; // start register address
data[1] = 0b11000000; // GPPUA
data[2] = 0b11111111; // GPPUB
twi_writeTo(TWI_ADDRESS, data, 3, True);
// set output pins high
// - rows : high : 1
// - other : low : 0 (or ignored)
data[0] = OLATA; // start register address
data[1] = 0b00111111; // OLATA
twi_writeTo(TWI_ADDRESS, data, 2, True);
}

View File

@ -0,0 +1,21 @@
/* ----------------------------------------------------------------------------
* ergoDOX controller: MCP23018 specific exports
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
#ifndef MCP23018_h
#define MCP23018_h
#define MCP23018_TWI_ADDRESS 0b0100000
void mcp23018_init(void);
#endif

View File

@ -0,0 +1,127 @@
# Documentation : MCP23018
## Pinouts and Pin assignments
* `+` indicates pin
* `o` indicates unused pin
* `-`s inserted between some of the pin functions for readability
* `OC**` pins enclosed in parenthesis had lines over them in the pinout
### MCP23018
Vss(GND) +01---.---28+ NC
NC +02 27+ GPA7
GPB0 +03 26+ GPA6
GPB1 +04 25+ GPA5
GPB2 +05 24+ GPA4
GPB3 +06 23+ GPA3
GPB4 +07 22+ GPA2
GPB5 +08 21+ GPA1
GPB6 +09 20+ GPA0
GPB7 +10 19+ INTA
Vdd(Vcc) +11 18+ INTB
SCL +12 17+ NC
SDA +13 16+ RESET
NC +14-------15+ ADDR
### MCP32018 Pin Assignments
power_negative Vss(GND) +01---.---28o
o02 27o
column0 GPB0 +03 26o
column1 GPB1 +04 25+ GPA5 rowB
column2 GPB2 +05 24+ GPA4 rowA
column3 GPB3 +06 23+ GPA3 row9
column4 GPB4 +07 22+ GPA2 row8
column5 GPB5 +08 21+ GPA1 row7
column6 GPB6 +09 20+ GPA0 row6
o10 19o
power_positive Vdd(Vcc) +11 18o
I2C SCL +12 17o
I2C SDA +13 16o
o14-------15+ ADDR (see note)
* notes:
* ADDR (pin15): Set slave address to `0b0100000` by connecting to Vss(GND).
(The user-defined bits are the three least significant).
## Notes about Registers
register address function (for all bits)
-------- ------- -----------------------
IODIRA 0x00 \ 1: set corresponding pin as input
IODIRB 0x01 / 0: set ................. as output
GPPUA 0x0C \ 1: set corresponding pin internal pull-up on
GPPUB 0x0D / 0: set .......................... pull-up off
GPIOA 0x12 \ read: returns the value on the port
GPIOB 0x13 / write: modifies the OLAT register
OLATA 0x14 \ read: returns the value of this register
OLATB 0x15 / write: modifies the output latches that control the
pins configured as output
* IOCON (see datasheet section 1.6, table 1-5, register 1-8)
* BANK: bit 7; read/write; default = 0
* 1: The registers associated with each port are separated into different
banks
* 0: The registers are in the same bank (addresses are sequential)
* SEQOP: bit 5; read/write; default = 0
* 1: Sequential operation disabled, address pointer does not increment
* 0: Sequential operation enabled, address pointer increments
* notes:
* All addresses given for IOCON.BANK = 0, since that's the default value of
the bit, and that's what we'll be using.
* abbreviations:
* IODIR = I/O Direction Register
* IOCON = I/O Control Register
* GPPU = GPIO Pull-Up Resistor Register
* GPIO = General Purpose I/O Port Register
* OLAT = Output Latch Register
## I&sup2;C Device Protocol (see datasheet section 1.3, figure 1-1)
S : Start OP : Device opcode
SR : Restart ADDR : Device address
P : Stop Dout : Data out from MCP23018
W : Write Din : Data in to MCP23018
R : Read
S OP W ADDR ----> Din ... Din --> P
|
|--> SR OP R Dout ... Dout ---> P
|<-------------------------|
|
|--> SR OP W ADDR ... Din --> P
|
|--> P
S OP R ----> Dout ... Dout --> P
|
|--> SR OP R Dout ... Dout ---> P
|<--------------------------|
|
|--> SR OP W ADDR Din ... Din --> P
|
|--> P
Byte and Sequential Write
-------------------------
Byte : S OP W ADDR --> Din --> P
Sequential : S OP W ADDR --> Din ... Din --> P
Byte and Sequential Read
------------------------
Byte : S OP W ADDR --> SR OP R Dout --> P
Sequential : S OP W ADDR --> SR OP R Dout ... Dout --> P
* notes:
* We'll be using sequential mode (ICON.SEQOP = 0; default) (see datasheet
section 1.3.1).
-------------------------------------------------------------------------------
Copyright &copy; 2012 Ben Blazak
Released under The MIT License (MIT) (see "license.md") at
<https://github.com/benblazak/ergodox-firmware>

View File

@ -0,0 +1,65 @@
/* ----------------------------------------------------------------------------
* ergoDOX controller: Teensy 2.0 specific code
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
// processor frequency (from <http://www.pjrc.com/teensy/prescaler.html>)
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
#define CPU_16MHz 0x00
#define CPU_8MHz 0x01
#define CPU_4MHz 0x02
#define CPU_2MHz 0x03
#define CPU_1MHz 0x04
#define CPU_500kHz 0x05
#define CPU_250kHz 0x06
#define CPU_125kHz 0x07
#define CPU_62kHz 0x08
// TWI
#define TWI_FREQ 400000 // (see "lib/twi.(h|c)")
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <avr/io.h>
#include "lib/arduino/twi.h"
#include "teensy-2-0.h"
void teensy_init(void) {
CPU_PRESCALE(CPU_16MHz); // speed should match F_CPU in makefile
// unused pins
DDRB &= ~0b00001110; // set B(1,2,3) as input
PORTB |= 0b00001110; // set B(1,2,3) internal pull-up enabled
DDRD &= ~0b01110000; // set D(4,5,6) as input
PORTD |= 0b01110000; // set D(4,5,6) internal pull-up enabled
DDRE &= ~0b01000000; // set E(6) as input
PORTE |= 0b01000000; // set E(6) internal pull-up enabled
// LEDs (see "PWM on ports OC1(A|B|C)" in "teensy-2-0.md")
DDRB |= 0b11100000; // set B(5,6,7) as output
TCCR1A = 0b10101001; // set and configure fast PWM
TCCR1B = 0b00001001; // set and configure fast PWM
// rows
DDRF |= 0b11110011; // set F(0,1,4,5,6,7) as output
PORTF |= 0b11110011; // set F(0,1,4,5,6,7) drive high
// columns
DDRB &= ~0b00010001; // set B(0,4) as input
PORTB |= 0b00010001; // set B(0,4) internal pull-up enabled
DDRC &= ~0b11000000; // set C(6,7) as input
PORTC |= 0b11000000; // set C(6,7) internal pull-up enabled
DDRD &= ~0b10001100; // set D(2,3,7) as input
PORTD |= 0b10001100; // set D(2,3,7) internal pull-up enabled
// I2C (TWI)
twi_init(); // (on pins D(0,1))
}

View File

@ -0,0 +1,36 @@
/* ----------------------------------------------------------------------------
* ergoDOX controller: Teensy 2.0 specific exports
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak
* Released under The MIT License (MIT) (see "license.md") at
* <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
#include <avr/io.h> // for the register macros
#ifndef TEENSY_2_0_h
#define TEENSY_2_0_h
// LED control
#define TEENSY_LED1_ON (OCR1A = 0xFF)
#define TEENSY_LED1_OFF (OCR1A = 0x00)
#define TEENSY_LED1_SET(n) (OCR1A = (uint8_t)(n))
#define TEENSY_LED1_SET_PERCENT(n) (OCR1A = (uint8_t)((n) * 0xFF))
#define TEENSY_LED2_ON (OCR1B = 0xFF)
#define TEENSY_LED2_OFF (OCR1B = 0x00)
#define TEENSY_LED2_SET(n) (OCR1B = (uint8_t)(n))
#define TEENSY_LED2_SET_PERCENT(n) (OCR1B = (uint8_t)((n) * 0xFF))
#define TEENSY_LED3_ON (OCR1C = 0xFF)
#define TEENSY_LED3_OFF (OCR1C = 0x00)
#define TEENSY_LED3_SET(n) (OCR1C = (uint8_t)(n))
#define TEENSY_LED3_SET_PERCENT(n) (OCR1C = (uint8_t)((n) * 0xFF))
void teensy_init(void);
#endif

View File

@ -0,0 +1,100 @@
# Documentation : Teensy 2.0
## Pinouts and Pin assignments
* `+` indicates pin
* `o` indicates unused pin
* `-`s inserted between some of the pin functions for readability
* `OC**` pins enclosed in parenthesis had lines over them in the pinout
### Teensy 2.0
GND +---.....---+ VCC
SS PB0 + + PF0 ADC0
SCLK PB1 + + PF1 ADC1
MOSI PB2 + + PF4 ADC4
MISO PB3 + + + + PF5 ADC5
RTS OC1C OC0A --------- PB7 + PE6 AREF + PF6 ADC6
OC0B INT0 SCL PD0 + AIN0 + PF7 ADC7
INT1 SDA PD1 + INT6 + PB6 ADC13 OC1B OC4B
RXD1 ----------- INT2 --- PD2 + + PB5 ADC12 OC1A (OC4B)
TXD1 ----------- INT3 --- PD3 + + PB4 ADC11
OC3A (OC4A) -------- PC6 + + PD7 ADC10 T0 -- OC4D
ICP3 ----- OC4A --------- PC7 +-+-+-+-+-+-+ PD6 ADC9 T1 - (OC4D) onboardLED
CTS XCK1 PD5 --/ | | | \-- PD4 ADC8 ------------ ICP1
VCC ------------------/ | \-------------- RST
GND --------------------/
### Teensy 2.0 Pin Assignments
power_negative GND +---.....---+ Vcc power_positive
column6 PB0 + + PF0 row6
o + PF1 row7
o + PF4 row8
o o o + PF5 row9
LED3 OC1C + + PF6 rowA
I2C SCL + + PF7 rowB
I2C SDA + + OC1B LED2
column3 PD2 + + OC1A LED1
column4 PD3 + + PB4 column0
column1 PC6 + + PD7 column5
column2 PC7 +-o-o-o-o-o-o
* notes:
* SCL and SDA: Need external pull-up resistors. Sometimes the internal
pull-ups are enough (see datasheet section 20.5.1), but i think for this
project we'll want external ones, in case people want to separate the
halves very far.
## Notes about Registers
### General I/O (see datasheet section 10.2.1)
DDRxn function PORTxn function
1 output 1 drive high
0 drive low
0 input 1 internal pull-up on
0 internal pull-up off
PINxn action function
write 1 toggles the value of PORTxn
read returns the logical value (1|0) of the pin
* notes:
* Unused pins should be set as input with internal pullup enabled (see
datasheet section 10.2.6).
* We want the row pins 'drive high' initially, and the column pins set as
input with internal pull-up. We'll cycle through driving the row pins low,
and checking the column pins in the update function.
### PWM on ports OC1(A|B|C) (see datasheet section 14.10)
* notes:
* PWM pins should be set as outputs.
* we want Waveform Generation Mode 5
(fast PWM, 8-bit)
(see table 14-5)
* set `TCCRB[4,3],TCCRA[1,0]` to `0,1,0,1`
* we want "Compare Output Mode, Fast PWM" to be `0b10`
"Clear OCnA/OCnB/OCnC on compare match, set OCnA/OCnB/OCnC at TOP"
(see table 14-3)
this way higher values of `OCR1(A|B|C)` will mean longer 'on' times for the
LEDs
* when in a fast PWM mode, set `TCCR1A[7,6,5,4,3,2]` to `1,0,1,0,1,0`
* we want "Clock Select Bit Description" to be `0b001`
"clkI/O/1 (No prescaling)"
(see table 14-6)
* set `TCCR1B[2,1,0]` to `0,0,1`
* LEDs will be at minimum brightness until OCR1(A|B|C) are changed (since
the default value of all the bits in those registers is 0)
* abbreviations:
* OCR = Output Compare Register
* TCCR = Timer/Counter Control Register
-------------------------------------------------------------------------------
Copyright &copy; 2012 Ben Blazak
Released under The MIT License (MIT) (see "license.md") at
<https://github.com/benblazak/ergodox-firmware>

View File

@ -15,8 +15,6 @@
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
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/
#include <math.h>
@ -25,7 +23,6 @@
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#include "Arduino.h" // for digitalWrite
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
@ -35,20 +32,17 @@
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#include "pins_arduino.h"
#include "twi.h"
static volatile uint8_t twi_state;
static volatile uint8_t twi_slarw;
static volatile uint8_t twi_sendStop; // should the transaction end with a stop
static volatile uint8_t twi_inRepStart; // in the middle of a repeated start
static uint8_t twi_slarw;
static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int);
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex;
static volatile uint8_t twi_masterBufferLength;
static uint8_t twi_masterBufferLength;
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex;
@ -69,17 +63,23 @@ void twi_init(void)
{
// initialize state
twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;
// activate internal pullups for twi.
digitalWrite(SDA, 1);
digitalWrite(SCL, 1);
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
// activate internal pull-ups for twi
// as per note from atmega8 manual pg167
sbi(PORTC, 4);
sbi(PORTC, 5);
#else
// activate internal pull-ups for twi
// as per note from atmega128 manual pg204
sbi(PORTD, 0);
sbi(PORTD, 1);
#endif
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
@ -109,10 +109,9 @@ void twi_setAddress(uint8_t address)
* Input address: 7bit i2c device address
* data: pointer to byte array
* length: number of bytes to read into array
* sendStop: Boolean indicating whether to send a stop at the end
* Output number of bytes read
*/
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
{
uint8_t i;
@ -126,7 +125,6 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
continue;
}
twi_state = TWI_MRX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
@ -143,20 +141,8 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
twi_slarw = TW_READ;
twi_slarw |= address << 1;
if (true == twi_inRepStart) {
// if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
TWDR = twi_slarw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
}
else
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// wait for read operation to complete
while(TWI_MRX == twi_state){
@ -182,14 +168,13 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
* data: pointer to byte array
* length: number of bytes in array
* wait: boolean indicating to wait for write or not
* sendStop: boolean indicating whether or not to send a stop at the end
* Output 0 .. success
* 1 .. length to long for buffer
* 2 .. address send, NACK received
* 3 .. data send, NACK received
* 4 .. other twi error (lost bus arbitration, bus error, ..)
*/
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait)
{
uint8_t i;
@ -203,7 +188,6 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
continue;
}
twi_state = TWI_MTX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
@ -220,23 +204,8 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
// if we're in a repeated start, then we've already sent the START
// in the ISR. Don't do it again.
//
if (true == twi_inRepStart) {
// if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
TWDR = twi_slarw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
}
else
// send start condition
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs
// send start condition
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// wait for write operation to complete
while(wait && (TWI_MTX == twi_state)){
@ -263,7 +232,7 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
* 2 not slave transmitter
* 0 ok
*/
uint8_t twi_transmit(const uint8_t* data, uint8_t length)
uint8_t twi_transmit(uint8_t* data, uint8_t length)
{
uint8_t i;
@ -380,16 +349,7 @@ SIGNAL(TWI_vect)
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1);
}else{
if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
twi_stop();
}
break;
case TW_MT_SLA_NACK: // address sent, nack received
@ -420,17 +380,6 @@ SIGNAL(TWI_vect)
case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
break;
case TW_MR_SLA_NACK: // address sent, nack received
twi_stop();
break;

View File

@ -24,6 +24,10 @@
//#define ATMEGA8
#ifndef CPU_FREQ
#define CPU_FREQ 16000000L
#endif
#ifndef TWI_FREQ
#define TWI_FREQ 100000L
#endif
@ -40,9 +44,9 @@
void twi_init(void);
void twi_setAddress(uint8_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t);
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t);
uint8_t twi_transmit(const uint8_t*, uint8_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t);
uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t);
uint8_t twi_transmit(uint8_t*, uint8_t);
void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
void twi_attachSlaveTxEvent( void (*)(void) );
void twi_reply(uint8_t);

View File

@ -8,9 +8,12 @@
# -----------------------------------------------------------------------------
SRC = firmware.c
SRC = $(shell find -name '*.c')
EXTRAINCDIRS = .
TEENSY_MAKE = $(MAKE) -f 'lib/teensy-makefile' SRC='$(SRC)'
TEENSY_MAKE = $(MAKE) -f 'lib/pjrc/Makefile' \
SRC='$(SRC)' \
EXTRAINCDIRS='$(EXTRAINCDIRS)'
.PHONY: all clean