Added Code & OpenSCAD files
This commit is contained in:
commit
de681b31bc
131
code/i2c_master.c
Normal file
131
code/i2c_master.c
Normal file
@ -0,0 +1,131 @@
|
||||
#include "i2c_master.h"
|
||||
#include <avr/io.h>
|
||||
|
||||
uint8_t i2c_clock(uint8_t type);
|
||||
|
||||
void i2c_init(void)
|
||||
{
|
||||
// Define SCL and SDA as Output
|
||||
DDRB |= (1 << 0) | (1 << 2);
|
||||
|
||||
// Preload dataregister with "released level" data
|
||||
USIDR = 0xFF;
|
||||
|
||||
// Use SCL and SDA pins
|
||||
// Select clock sources
|
||||
USICR = (1 << USIWM1) | (1 << USICS1) | (1 << USICLK);
|
||||
|
||||
// Clear flags and reset counter
|
||||
USISR = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC);
|
||||
}
|
||||
|
||||
uint8_t i2c_start(uint8_t address)
|
||||
{
|
||||
// FOR REPEATED START
|
||||
// Release SCL
|
||||
PORTB |= (1 << 2);
|
||||
// Verify that SCL becomes high
|
||||
while (!( PINB & (1 << 2) ));
|
||||
|
||||
// GENERATE START CONDITION
|
||||
// Force SDA LOW
|
||||
PORTB &= ~(1 << 0);
|
||||
|
||||
// Pull SCL LOW
|
||||
PORTB &= ~(1 << 2);
|
||||
// Release SDA
|
||||
PORTB |= (1 << 0);
|
||||
|
||||
return i2c_write(address);
|
||||
}
|
||||
|
||||
uint8_t i2c_write(uint8_t data)
|
||||
{
|
||||
// Pull SCL LOW
|
||||
PORTB &= ~(1 << 2);
|
||||
// Setup data
|
||||
USIDR = data;
|
||||
|
||||
// Send 8 bits on bus
|
||||
i2c_clock(8);
|
||||
|
||||
// Enable SDA as input
|
||||
DDRB &= ~(1 << 0);
|
||||
|
||||
// Receive 1 bit on bus & Check for NACK
|
||||
if (i2c_clock(1) & (1 << 0))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t i2c_read(uint8_t nack)
|
||||
{
|
||||
// Enable SDA as input.
|
||||
DDRB &= ~(1 << 0);
|
||||
// Read 8 bits
|
||||
uint8_t result = i2c_clock(8);
|
||||
|
||||
if (nack)
|
||||
{
|
||||
// Load NACK
|
||||
USIDR = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load ACK
|
||||
USIDR = 0x00;
|
||||
}
|
||||
|
||||
i2c_clock(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void i2c_stop(void)
|
||||
{
|
||||
// Pull SDA low
|
||||
PORTB &= ~(1 << 0);
|
||||
// Release SCL
|
||||
PORTB |= (1 << 2);
|
||||
// Wait for SCL to go high
|
||||
while (!( PINB & (1 << 2) ));
|
||||
|
||||
// Release SDA
|
||||
PORTB |= (1 << 0);
|
||||
}
|
||||
|
||||
uint8_t i2c_clock(uint8_t count)
|
||||
{
|
||||
uint8_t reg_temp = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC);
|
||||
// Counter counts number of edges
|
||||
// Overflow signals end of transmission
|
||||
reg_temp |= 16 - (count * 2);
|
||||
|
||||
USISR = reg_temp;
|
||||
|
||||
// Set clock source & toggle clock prepare
|
||||
reg_temp = (1 << USIWM1) | (1 << USICS1) | (1 << USICLK) | (1 << USITC);
|
||||
do
|
||||
{
|
||||
// Generate positve SCL edge.
|
||||
USICR = reg_temp;
|
||||
|
||||
// Wait for SCL to go high
|
||||
while (!( PINB & (1 << 2)));
|
||||
|
||||
// Generate negative SCL edge
|
||||
USICR = reg_temp;
|
||||
}
|
||||
// Wait for counter overflow (all edges are completed)
|
||||
while (!( USISR & (1 << USIOIF)));
|
||||
|
||||
// Read data
|
||||
reg_temp = USIDR;
|
||||
// Load dataregister with "released level" data
|
||||
USIDR = 0xFF;
|
||||
|
||||
// Enable SDA as output
|
||||
DDRB |= (1 << 0);
|
||||
return reg_temp;
|
||||
}
|
||||
15
code/i2c_master.h
Normal file
15
code/i2c_master.h
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef I2C_MASTER_H
|
||||
#define I2C_MASTER_H
|
||||
|
||||
#define I2C_READ 0x01
|
||||
#define I2C_WRITE 0x00
|
||||
|
||||
void i2c_init(void);
|
||||
uint8_t i2c_start(uint8_t address);
|
||||
uint8_t i2c_write(uint8_t data);
|
||||
uint8_t i2c_read(uint8_t nack);
|
||||
void i2c_stop(void);
|
||||
|
||||
#endif // I2C_MASTER_H
|
||||
848
code/main.c
Normal file
848
code/main.c
Normal file
@ -0,0 +1,848 @@
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "i2c_master.h"
|
||||
|
||||
#define EEPROM_SIZE 512
|
||||
|
||||
#define CHAR_SIZE 5
|
||||
|
||||
#define OLED_ADDRESS (0x3C << 1)
|
||||
|
||||
#define OLED_X_SIZE 128
|
||||
#define OLED_Y_SIZE 32
|
||||
|
||||
// Time(ms) to keep the display on before sleep
|
||||
#define DISPLAY_DELAY 3000
|
||||
|
||||
// Time(ms) to assume long button press
|
||||
#define LONG_PRESS 500
|
||||
|
||||
// Rotary Encoder Parameters
|
||||
#define ROT_PULSE_COUNT 12
|
||||
#define ROT_WHEEL_RAD 12
|
||||
|
||||
// Input masks
|
||||
#define BUTTON_V 0x01
|
||||
#define BUTTON_S 1
|
||||
#define BUTTON_M (BUTTON_V << BUTTON_S)
|
||||
|
||||
#define ENCODER_V 0x03
|
||||
#define ENCODER_S 3
|
||||
#define ENCODER_M (ENCODER_V << ENCODER_S)
|
||||
|
||||
#define INPUT_MASK (BUTTON_M | ENCODER_M)
|
||||
|
||||
// Event queue macros
|
||||
#define MAX_EVENT_COUNT 16
|
||||
#define PTR_INC(x) ((x) = events + ((((x) - events) + 1) % MAX_EVENT_COUNT))
|
||||
|
||||
enum event_e
|
||||
{
|
||||
EVENT_BUTTON_DOWN,
|
||||
EVENT_BUTTON_UP,
|
||||
EVENT_ROT_CW,
|
||||
EVENT_ROT_CCW,
|
||||
EVENT_NONE
|
||||
};
|
||||
|
||||
enum state_e
|
||||
{
|
||||
STATE_COUNTING,
|
||||
STATE_SETTING
|
||||
};
|
||||
|
||||
uint8_t old_input_state;
|
||||
|
||||
uint8_t events[MAX_EVENT_COUNT];
|
||||
uint8_t event_count = 0;
|
||||
uint8_t *event_read = events;
|
||||
uint8_t *event_write = events;
|
||||
|
||||
uint32_t ms = 0;
|
||||
|
||||
const uint8_t PROGMEM rot_table[16] =
|
||||
{
|
||||
EVENT_NONE, EVENT_ROT_CW, EVENT_ROT_CCW, EVENT_NONE,
|
||||
EVENT_ROT_CCW, EVENT_NONE, EVENT_NONE, EVENT_ROT_CW,
|
||||
EVENT_ROT_CW, EVENT_NONE, EVENT_NONE, EVENT_ROT_CCW,
|
||||
EVENT_NONE, EVENT_ROT_CCW, EVENT_ROT_CW, EVENT_NONE
|
||||
};
|
||||
|
||||
const uint8_t PROGMEM symbols[80] =
|
||||
{
|
||||
0x3D, 0x51, 0x49, 0x45, 0x3D, // 0
|
||||
0x00, 0x42, 0x7F, 0x40, 0x00, // 1
|
||||
0x42, 0x61, 0x51, 0x49, 0x46, // 2
|
||||
0x21, 0x41, 0x45, 0x4B, 0x31, // 3
|
||||
0x18, 0x14, 0x12, 0x7F, 0x10, // 4
|
||||
0x27, 0x45, 0x45, 0x45, 0x39, // 5
|
||||
0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
|
||||
0x03, 0x71, 0x09, 0x05, 0x03, // 7
|
||||
0x36, 0x49, 0x49, 0x49, 0x36, // 8
|
||||
0x06, 0x49, 0x49, 0x29, 0x1D, // 9
|
||||
0x7C, 0x04, 0x78, 0x04, 0x78, // m
|
||||
0x1F, 0x20, 0x40, 0x20, 0x1F, // V
|
||||
0x00, 0x60, 0x60, 0x00, 0x00, // .
|
||||
0x00, 0x00, 0xFF, 0x00, 0x00, // |
|
||||
0x04, 0x06, 0xFF, 0x06, 0x04, // Up-arrow
|
||||
0x20, 0x60, 0xFF, 0x60, 0x20 // Down-arrow
|
||||
};
|
||||
|
||||
void simple_init();
|
||||
void display_send_cmd(uint8_t cmd);
|
||||
void display_send_data(const uint8_t *data, uint16_t buflen);
|
||||
void display_init();
|
||||
void display_enable(uint8_t en);
|
||||
void get_symbol16(uint8_t index, uint8_t *out);
|
||||
void print_symbol(uint8_t symbol_idx, uint8_t x, uint8_t y, uint8_t invert);
|
||||
void print_mm(uint32_t value, uint32_t highlight);
|
||||
void print_direction(uint8_t is_up);
|
||||
void print_voltage(uint16_t value);
|
||||
uint16_t adc_measure();
|
||||
uint16_t get_voltage();
|
||||
uint16_t find_eeprom_idx();
|
||||
void read_eeprom_val(uint16_t idx, uint32_t *value, uint8_t *dir);
|
||||
void write_eeprom_val(uint16_t idx, uint32_t value, uint8_t dir);
|
||||
void do_sleep();
|
||||
void update_display(uint32_t value, uint32_t highlight, uint8_t dir, uint8_t dir_highlight);
|
||||
|
||||
ISR(TIMER1_OVF_vect)
|
||||
{
|
||||
cli();
|
||||
++ms;
|
||||
sei();
|
||||
}
|
||||
|
||||
ISR(PCINT0_vect)
|
||||
{
|
||||
// Check button and encoder inputs
|
||||
uint8_t input_state = PINB & INPUT_MASK;
|
||||
uint8_t rot_event = EVENT_NONE;
|
||||
uint8_t rot_idx;
|
||||
|
||||
cli();
|
||||
|
||||
// Check if button has changed
|
||||
if (((input_state ^ old_input_state) & BUTTON_M) &&
|
||||
(event_count != MAX_EVENT_COUNT))
|
||||
{
|
||||
// Button is unpressed
|
||||
if (input_state & BUTTON_M)
|
||||
{
|
||||
*event_write = EVENT_BUTTON_UP;
|
||||
}
|
||||
else
|
||||
{
|
||||
*event_write = EVENT_BUTTON_DOWN;
|
||||
}
|
||||
++event_count;
|
||||
PTR_INC(event_write);
|
||||
}
|
||||
|
||||
// Check if encoder has changed
|
||||
if (((input_state ^ old_input_state) & ENCODER_M) &&
|
||||
(event_count != MAX_EVENT_COUNT))
|
||||
{
|
||||
rot_idx = ((old_input_state & ENCODER_M) >> (ENCODER_S - 2)) |
|
||||
((input_state & ENCODER_M) >> ENCODER_S);
|
||||
rot_event = pgm_read_byte(&(rot_table[rot_idx]));
|
||||
|
||||
if (EVENT_NONE != rot_event)
|
||||
{
|
||||
*event_write = rot_event;
|
||||
++event_count;
|
||||
PTR_INC(event_write);
|
||||
}
|
||||
}
|
||||
|
||||
old_input_state = input_state;
|
||||
|
||||
sei();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// 1 / (4 * ROT_PULSE_COUNT) * (2 * pi * ROT_WHEEL_RAD)
|
||||
const float rot_coeff = 1.57f * ROT_WHEEL_RAD / ROT_PULSE_COUNT;
|
||||
uint32_t sleep_when_ms = 0;
|
||||
uint32_t long_press_when_ms = 0;
|
||||
uint32_t count_value = 0;
|
||||
float count_value_fine = 0;
|
||||
uint32_t eeprom_value = 0;
|
||||
uint16_t eeprom_idx = 0;
|
||||
uint8_t move_dir = 0;
|
||||
int8_t rot_value = 0;
|
||||
|
||||
uint32_t highlight;
|
||||
uint8_t dir_highlight;
|
||||
uint8_t curr_event;
|
||||
uint8_t state;
|
||||
|
||||
// Init Direct Hardware
|
||||
simple_init();
|
||||
|
||||
i2c_init();
|
||||
display_init();
|
||||
|
||||
state = STATE_COUNTING;
|
||||
|
||||
eeprom_idx = find_eeprom_idx();
|
||||
if (EEPROM_SIZE == eeprom_idx)
|
||||
{
|
||||
eeprom_idx = 0;
|
||||
state = STATE_SETTING;
|
||||
}
|
||||
|
||||
read_eeprom_val(eeprom_idx, &eeprom_value, &move_dir);
|
||||
if (0 == eeprom_value)
|
||||
{
|
||||
state = STATE_SETTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
count_value = eeprom_value;
|
||||
count_value_fine = eeprom_value;
|
||||
}
|
||||
|
||||
if (STATE_COUNTING == state)
|
||||
{
|
||||
display_enable(0);
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case STATE_COUNTING:
|
||||
if (event_count > 0)
|
||||
{
|
||||
// Consume Event
|
||||
cli();
|
||||
curr_event = *event_read;
|
||||
PTR_INC(event_read);
|
||||
--event_count;
|
||||
sei();
|
||||
|
||||
// Process Event
|
||||
switch(curr_event)
|
||||
{
|
||||
case EVENT_BUTTON_DOWN:
|
||||
long_press_when_ms = ms + LONG_PRESS;
|
||||
sleep_when_ms = ms + DISPLAY_DELAY;
|
||||
display_enable(1);
|
||||
update_display(count_value, 0, move_dir, 0);
|
||||
break;
|
||||
|
||||
case EVENT_BUTTON_UP:
|
||||
long_press_when_ms = 0;
|
||||
break;
|
||||
|
||||
case EVENT_ROT_CW:
|
||||
if (0 != move_dir)
|
||||
{
|
||||
--rot_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
++rot_value;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_ROT_CCW:
|
||||
if (0 != move_dir)
|
||||
{
|
||||
++rot_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
--rot_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rot_value / 2 != 0)
|
||||
{
|
||||
int8_t rot_abs;
|
||||
int8_t adjust;
|
||||
|
||||
rot_abs = rot_value;
|
||||
adjust = 1;
|
||||
if (rot_value < 0)
|
||||
{
|
||||
rot_abs = -rot_abs;
|
||||
adjust = -1;
|
||||
}
|
||||
|
||||
count_value_fine += (rot_coeff * adjust * rot_abs);
|
||||
count_value = (uint32_t) count_value_fine;
|
||||
rot_value -= (rot_abs * adjust);
|
||||
|
||||
if (0 != sleep_when_ms)
|
||||
{
|
||||
update_display(count_value, 0, move_dir, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ((0 != long_press_when_ms) && (long_press_when_ms < ms))
|
||||
{
|
||||
sleep_when_ms = 0;
|
||||
long_press_when_ms = 0;
|
||||
highlight = 100000;
|
||||
update_display(count_value, highlight, move_dir, 0);
|
||||
state = STATE_SETTING;
|
||||
}
|
||||
|
||||
if ((0 != sleep_when_ms) && (sleep_when_ms < ms))
|
||||
{
|
||||
sleep_when_ms = 0;
|
||||
display_enable(0);
|
||||
do_sleep();
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_SETTING:
|
||||
if (event_count > 0)
|
||||
{
|
||||
// Consume Event
|
||||
cli();
|
||||
curr_event = *event_read;
|
||||
PTR_INC(event_read);
|
||||
--event_count;
|
||||
sei();
|
||||
|
||||
// Process Event
|
||||
switch(curr_event)
|
||||
{
|
||||
case EVENT_BUTTON_DOWN:
|
||||
long_press_when_ms = ms + LONG_PRESS;
|
||||
display_enable(1);
|
||||
break;
|
||||
|
||||
case EVENT_BUTTON_UP:
|
||||
if (long_press_when_ms > ms)
|
||||
{
|
||||
// Short Press
|
||||
|
||||
if (0 != dir_highlight)
|
||||
{
|
||||
if (0 == move_dir)
|
||||
{
|
||||
move_dir = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
move_dir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != highlight)
|
||||
{
|
||||
uint32_t temp_count = count_value;
|
||||
temp_count = count_value % (highlight * 10);
|
||||
count_value -= temp_count;
|
||||
temp_count += highlight;
|
||||
temp_count %= highlight * 10;
|
||||
count_value += temp_count;
|
||||
count_value_fine = count_value;
|
||||
}
|
||||
|
||||
update_display(count_value, highlight, move_dir, dir_highlight);
|
||||
}
|
||||
long_press_when_ms = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((0 != long_press_when_ms) && (long_press_when_ms < ms))
|
||||
{
|
||||
// Long press
|
||||
|
||||
if (0 != highlight)
|
||||
{
|
||||
highlight /= 10;
|
||||
if (0 == highlight)
|
||||
{
|
||||
dir_highlight = 1;
|
||||
}
|
||||
}
|
||||
else if (0 != dir_highlight)
|
||||
{
|
||||
dir_highlight = 0;
|
||||
state = STATE_COUNTING;
|
||||
|
||||
++eeprom_idx;
|
||||
eeprom_idx %= EEPROM_SIZE;
|
||||
write_eeprom_val(idx, count_value, move_dir);
|
||||
}
|
||||
|
||||
update_display(count_value, highlight, move_dir, dir_highlight);
|
||||
long_press_when_ms = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void simple_init()
|
||||
{
|
||||
// Set pull-ups on button and rotary encoder
|
||||
PORTB = (1 << 1) | (1 << 3) | (1 << 4);
|
||||
|
||||
// Prescaler 1024
|
||||
TCCR1 = (1 << CS13) | (1 << CS11) | (1 << CS10);
|
||||
|
||||
// Enable Interrupt on overflow
|
||||
TIMSK = (1 << TOIE1);
|
||||
|
||||
// Interrupt on button and rotary encoder pins
|
||||
PCMSK = (1 << PCINT1) | (1 << PCINT3) | (1 << PCINT4);
|
||||
|
||||
// Interrupt type is any logical change
|
||||
MCUCR = (1 << ISC00);
|
||||
|
||||
// Enable Interrupt on pin-change
|
||||
GIMSK = (1 << PCIE);
|
||||
|
||||
// Measure Vbg(1.1V) with Vcc reference
|
||||
ADMUX = (1 << MUX3) | (1 << MUX2);
|
||||
|
||||
// ADC prescaler 16
|
||||
ADCSRA = (1 << ADPS2);
|
||||
|
||||
// Power reduction - Disable timer 0
|
||||
PRR = (1 << PRTIM0);
|
||||
|
||||
old_input_state = INPUT_MASK;
|
||||
}
|
||||
|
||||
void display_send_cmd(uint8_t cmd)
|
||||
{
|
||||
i2c_start(OLED_ADDRESS | I2C_WRITE);
|
||||
i2c_write(0x00); // Command Indicator
|
||||
i2c_write(cmd);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void display_send_data(const uint8_t *data, uint16_t buflen)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
for (i = 0; i < buflen; ++i)
|
||||
{
|
||||
if (0 == (i % 0x0F))
|
||||
{
|
||||
i2c_start(OLED_ADDRESS | I2C_WRITE);
|
||||
i2c_write(0x40); // Data Indicator
|
||||
}
|
||||
|
||||
i2c_write(data[i]);
|
||||
|
||||
if (0x0F == (i % 0x0F))
|
||||
{
|
||||
i2c_stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != (i % 0x0F))
|
||||
{
|
||||
i2c_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void display_init()
|
||||
{
|
||||
uint16_t i;
|
||||
uint8_t buf[16];
|
||||
|
||||
const uint8_t cmd_list[] =
|
||||
{
|
||||
0xAE, // Display OFF
|
||||
0xD5, // Set Clock Divider / Oscillator Frequency
|
||||
0x80, // Default OSC / No Div
|
||||
0xA8, // Set Multiplex Ratio
|
||||
OLED_Y_SIZE - 1,
|
||||
0xD3, // Set Display Offset
|
||||
0x00, // 0 Offset
|
||||
0x40 | 0x00, // Set Start Line To 0
|
||||
0x8D, // Set Charge Pump
|
||||
0x14, // Enable Charge Pump When Display Is On
|
||||
0x20, // Set Memory Mode
|
||||
0x00, // Horizontal Mode
|
||||
0xA0 | 0x01, // Set Segment Remap - column 127 is seg 0
|
||||
0xC8, // Set COM Scan Direction To Decrement
|
||||
0xDA, // Set COM Pin Configuration
|
||||
0x02 | 0x00, // Row 0 = COM63, Row 31 = COM32
|
||||
0x81, // Set Contrast
|
||||
0xFF, // Contrast Value
|
||||
0xD9, // Set Precharge Period
|
||||
0x01 | 0xF0, // Phase 1 - 1 CLK, Phase 2 - 15 CLK
|
||||
0xDB, // Set VCOM Deselect Level
|
||||
#if 1
|
||||
0x30, // 0.83 x Vcc
|
||||
#else
|
||||
0x40, // UNKNOWN (0.90 x Vcc Assumed)
|
||||
#endif
|
||||
0xA4, // Display RAM Contents
|
||||
0xA6, // Set Normal Display (1 is white)
|
||||
0x2E, // Deactivate Scroll
|
||||
0xAF, // Display ON
|
||||
|
||||
// Commands to send to RAM
|
||||
0x21, // Set Column Address
|
||||
0x00,
|
||||
OLED_X_SIZE - 1,
|
||||
0x22, // Set Page Address
|
||||
0x00,
|
||||
(OLED_Y_SIZE / 8) - 1
|
||||
};
|
||||
|
||||
// Send Commands
|
||||
for (i = 0; i < sizeof(cmd_list); ++i)
|
||||
{
|
||||
display_send_cmd(cmd_list[i]);
|
||||
}
|
||||
|
||||
// Fill Buffer With Black
|
||||
for (i = 0; i < sizeof(buf); ++i)
|
||||
{
|
||||
buf[i] = 0;
|
||||
}
|
||||
|
||||
// Black Entire Screen
|
||||
for (i = 0; i < OLED_X_SIZE * OLED_Y_SIZE / 8 / sizeof(buf); ++i)
|
||||
{
|
||||
display_send_data(buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
|
||||
void display_enable(uint8_t en)
|
||||
{
|
||||
display_send_cmd(0xAE | (en & 0x01));
|
||||
}
|
||||
|
||||
void get_symbol16(uint8_t index, uint8_t *out)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t data_byte;
|
||||
const uint8_t PROGMEM unfold_table[16] =
|
||||
{
|
||||
0x00,
|
||||
0x02,
|
||||
0x08,
|
||||
0x0A,
|
||||
0x20,
|
||||
0x22,
|
||||
0x28,
|
||||
0x2A,
|
||||
0x80,
|
||||
0x82
|
||||
0x88,
|
||||
0x8A,
|
||||
0xA0,
|
||||
0xA2,
|
||||
0xA8,
|
||||
0xAA
|
||||
};
|
||||
|
||||
for (i = 0; i < CHAR_SIZE * 4; ++i)
|
||||
{
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < CHAR_SIZE; ++i)
|
||||
{
|
||||
data_byte = pgm_read_byte(&(symbols[index * CHAR_SIZE + i]));
|
||||
out[i * 2 + 1] = pgm_read_byte(&(unfold_table[data_byte & 0x0F]));
|
||||
out[CHAR_SIZE * 2 + i * 2 + 1] = pgm_read_byte(&(unfold_table[(data_byte >> 4) & 0x0F]));
|
||||
}
|
||||
}
|
||||
|
||||
void print_symbol(uint8_t symbol_idx, uint8_t x, uint8_t y, uint8_t invert)
|
||||
{
|
||||
uint8_t unfolded_symbol[CHAR_SIZE * 4];
|
||||
uint8_t cmd_list[6]
|
||||
{
|
||||
0x21, // Set Column Address
|
||||
0x00, // Start Address
|
||||
0x00, // End Address
|
||||
0x22, // Set Page Address
|
||||
0x00, // Start Page 0
|
||||
0x00 // End Page
|
||||
};
|
||||
uint8_t i;
|
||||
|
||||
get_symbol16(symbol_idx, unfolded_symbol);
|
||||
if (invert)
|
||||
{
|
||||
for (i = 0; i < sizeof(unfolded_symbol); ++i)
|
||||
{
|
||||
unfolded_symbol[i] = ~(unfolded_symbol[i]);
|
||||
}
|
||||
}
|
||||
|
||||
x = x % (OLED_X_SIZE);
|
||||
y = y % (OLED_Y_SIZE / (8 * 2));
|
||||
|
||||
if (x > (OLED_X_SIZE - CHAR_SIZE * 2)
|
||||
{
|
||||
x = OLED_X_SIZE - CHAR_SIZE * 2;
|
||||
}
|
||||
|
||||
cmd_list[1] = x;
|
||||
cmd_list[2] = x + CHAR_SIZE * 2 - 1;
|
||||
cmd_list[4] = y * 2;
|
||||
cmd_list[5] = y * 2 + 1;
|
||||
|
||||
for (i = 0; i < sizeof(cmd_list); ++i)
|
||||
{
|
||||
display_send_cmd(cmd_list[i]);
|
||||
}
|
||||
|
||||
display_send_data(unfolded_symbol, sizeof(unfolded_symbol));
|
||||
}
|
||||
|
||||
void print_mm(uint32_t value, uint32_t highlight)
|
||||
{
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t symbol;
|
||||
uint8_t invert;
|
||||
uint8_t is_first;
|
||||
uint32_t div_factor;
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
div_factor = 1000000;
|
||||
value %= div_factor;
|
||||
div_factor /= 10;
|
||||
is_first = 1;
|
||||
|
||||
// Print 6 digits
|
||||
while (div_factor != 0)
|
||||
{
|
||||
symbol = value / div_factor;
|
||||
value %= div_factor;
|
||||
|
||||
if (div_factor == highlight)
|
||||
{
|
||||
invert = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
invert = 0;
|
||||
}
|
||||
|
||||
// Check if we need to print (for leading zeroes)
|
||||
if (!(is_first && (0 == highlight) && (0 == symbol)))
|
||||
{
|
||||
is_first = 0;
|
||||
print_symbol(symbol * CHAR_SIZE, x, y, invert);
|
||||
}
|
||||
x += (CHAR_SIZE + 1) * 2;
|
||||
div_factor /= 10;
|
||||
}
|
||||
|
||||
// Print 'mm'
|
||||
print_symbol(10 * CHAR_SIZE, x, y, 0);
|
||||
x += (CHAR_SIZE + 1) * 2;
|
||||
print_symbol(10 * CHAR_SIZE, x, y, 0);
|
||||
}
|
||||
|
||||
void print_direction(uint8_t is_up, uint8_t invert)
|
||||
{
|
||||
uint8_t symbol_index[2];
|
||||
|
||||
if (is_up)
|
||||
{
|
||||
symbol_index[0] = 14 * CHAR_SIZE;
|
||||
symbol_index[1] = 13 * CHAR_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
symbol_index[0] = 13 * CHAR_SIZE;
|
||||
symbol_index[1] = 15 * CHAR_SIZE;
|
||||
}
|
||||
|
||||
print_symbol(symbol_index[0], OLED_X_SIZE - CHAR_SIZE * 2, 0, invert);
|
||||
print_symbol(symbol_index[1], OLED_X_SIZE - CHAR_SIZE * 2, 1, invert);
|
||||
}
|
||||
|
||||
void print_voltage(uint16_t value)
|
||||
{
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t symbol;
|
||||
uint32_t div_factor;
|
||||
|
||||
x = 0;
|
||||
y = 1;
|
||||
div_factor = 1000;
|
||||
value %= div_factor;
|
||||
div_factor /= 10;
|
||||
|
||||
// Print 3 digits
|
||||
while (div_factor != 0)
|
||||
{
|
||||
symbol = value / div_factor;
|
||||
value %= div_factor;
|
||||
|
||||
print_symbol(symbol * CHAR_SIZE, x, y, 0);
|
||||
if (0 == x)
|
||||
{
|
||||
x += (CHAR_SIZE + 1) * 2;
|
||||
}
|
||||
x += (CHAR_SIZE + 1) * 2;
|
||||
div_factor /= 10;
|
||||
}
|
||||
|
||||
// Print 'V'
|
||||
print_symbol(11 * CHAR_SIZE, x, y, 0);
|
||||
|
||||
// Print '.'
|
||||
x = (CHAR_SIZE + 1) * 2;
|
||||
print_symbol(12 * CHAR_SIZE, x, y, 0);
|
||||
}
|
||||
|
||||
uint16_t adc_measure()
|
||||
{
|
||||
uint16_t result;
|
||||
|
||||
// Enable and start ADC
|
||||
ADCSRA |= (1 << ADEN) | (1 << ADSC);
|
||||
|
||||
// Wait for conversion
|
||||
while (ADCSRA & (1 << ADSC));
|
||||
|
||||
// Read ADC value
|
||||
result = ADC;
|
||||
|
||||
// Disable ADC
|
||||
ADCSRA &= ~(1 << ADEN);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t measure_voltage()
|
||||
{
|
||||
uint32_t val;
|
||||
uint16_t result;
|
||||
|
||||
val = 110 * 1023;
|
||||
val /= adc_measure();
|
||||
result = val;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t find_eeprom_idx()
|
||||
{
|
||||
uint16_t idx = 0;
|
||||
uint8_t found = 0;
|
||||
|
||||
for (idx = 0; idx < EEPROM_SIZE; ++idx)
|
||||
{
|
||||
if (0xFF != eeprom_read_byte((uint8_t *) idx))
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
return EEPROM_SIZE;
|
||||
}
|
||||
|
||||
if (0 == idx)
|
||||
{
|
||||
for (idx = EEPROM_SIZE - 3; idx < EEPROM_SIZE; ++idx)
|
||||
{
|
||||
if (0xFF != eeprom_read_byte((uint8_t *) idx))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idx += EEPROM_SIZE;
|
||||
--idx;
|
||||
idx %= EEPROM_SIZE;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void read_eeprom_val(uint16_t idx, uint32_t *value, uint8_t *dir)
|
||||
{
|
||||
uint8_t curr_byte;
|
||||
uint8_t i;
|
||||
|
||||
(*value) = 0;
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
curr_byte = eeprom_read_byte((uint8_t *) idx);
|
||||
(*value) |= (curr_byte << (24 - 8 * i));
|
||||
++idx;
|
||||
idx %= EEPROM_SIZE;
|
||||
}
|
||||
|
||||
*dir = ((*value) & 0x00800000) >> 23;
|
||||
(*value) &= 0x007FFFFF;
|
||||
}
|
||||
|
||||
void write_eeprom_val(uint16_t idx, uint32_t value, uint8_t dir)
|
||||
{
|
||||
uint8_t curr_byte;
|
||||
uint8_t i;
|
||||
|
||||
value |= 0xFF000000;
|
||||
if (0 != dir)
|
||||
{
|
||||
value |= 0x00800000;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
curr_byte = (value >> (24 - 8 * i)) & 0xFF;
|
||||
eeprom_update_byte((uint8_t *) idx, curr_byte);
|
||||
++idx;
|
||||
idx %= EEPROM_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
void do_sleep()
|
||||
{
|
||||
// Disable clock to Timer1, USI, ADC
|
||||
PRR |= (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
||||
|
||||
// Enable sleep
|
||||
MCUCR |= (1 << SE);
|
||||
|
||||
// Sleep
|
||||
sleep_cpu();
|
||||
|
||||
// Reset ms counter
|
||||
cli();
|
||||
ms = 0;
|
||||
sei();
|
||||
|
||||
// Disable sleep
|
||||
MCUCR &= ~(1 << SE);
|
||||
|
||||
// Enable clock to Timer1, USI, ADC
|
||||
PRR &= ~((1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC));
|
||||
}
|
||||
|
||||
void update_display(uint32_t value, uint32_t highlight, uint8_t dir, uint8_t dir_highlight)
|
||||
{
|
||||
print_mm(value, highlight);
|
||||
print_direction(dir, dir_highlight);
|
||||
print_voltage(get_voltage());
|
||||
}
|
||||
37
openscad/spool_holder.scad
Normal file
37
openscad/spool_holder.scad
Normal file
@ -0,0 +1,37 @@
|
||||
thickness = 5;
|
||||
hole_rad = 5;
|
||||
spool_rad = 150;
|
||||
square_size = spool_rad + hole_rad * 4;
|
||||
triangle_rad = square_size * 2 / 3;
|
||||
filament_width = 10;
|
||||
filament_height = 20;
|
||||
filament_offset = 2;
|
||||
// -1 = Left; 1 = Right
|
||||
side = -1;
|
||||
|
||||
difference()
|
||||
{
|
||||
// Base Shape
|
||||
union()
|
||||
{
|
||||
translate([square_size / 2 * side, 0, 0])
|
||||
cube([square_size, square_size, thickness], center = true);
|
||||
|
||||
translate([0, -square_size / 6, 0])
|
||||
rotate([0, 0, -30])
|
||||
cylinder(thickness, triangle_rad, triangle_rad, $fn = 3, center = true);
|
||||
}
|
||||
|
||||
// Holes
|
||||
for (i = [0:1:2])
|
||||
{
|
||||
translate([0, -square_size / 6, 0])
|
||||
rotate(-30 + i*120)
|
||||
translate([triangle_rad - hole_rad * 3, 0, 0])
|
||||
cylinder(thickness + 2, hole_rad, hole_rad, $fn = 360, center = true);
|
||||
}
|
||||
|
||||
// Filament Sensor
|
||||
translate([square_size * side - side * (filament_width / 2 + filament_offset), square_size / 2 - filament_height / 2, thickness / 2])
|
||||
cube([filament_width, filament_height, thickness], center = true);
|
||||
}
|
||||
6
openscad/zippy_cover.scad
Normal file
6
openscad/zippy_cover.scad
Normal file
@ -0,0 +1,6 @@
|
||||
difference()
|
||||
{
|
||||
cylinder(5, 11, 11, $fn=360, center = true);
|
||||
translate([0, 0, 1.5])
|
||||
cylinder(5, 10.5, 10.5, $fn=360, center = true);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user