В продолжение к прошлой статье решил пощупать и Attiny10. Ну меньше уже точно ничего нет. Если и есть такое извращение с менее чем 6 ногами, я о нем не знаю, точнее не нашел.
Тут у нас полноценный МК, в корпусе SOT-23-6! И задачи на нем решать можно вполне серьезные. Собрав схему на макетке с МК на адаптере и модулем дисплея я было обрадовался, но готовая плата работать отказалась...
А как, а что...
Attiny10 самый маленький МК, из AVR точно (не пугайтесь картинке, Microchip купила Atmel). Но характеристики у него вполне серьезные. Частота до 12МГц, 1кБ флэш и 32 байта оперативной памяти. Для корпуса SOT-23-6 это, согласитесь, не мало. Но самым главным плюсом наряду с размерами является энергопотребление, которое составляет всего 2.7-4мА при 8МГц и 5В питания, или всего 0.2-0.4мА при 1МГц и питании 1.8В.
Есть даже АЦП и ШИМ. Все характеристики вы можете посмотреть в даташите. Есть у тини10 и младшие братья ATTINY4, 5 и 9. У 4 и 9 нет АЦП, у 4 и 5 к тому же всего 512 байт флэш.
Программирование
Программируются эти малыши по интерфейсу TPI- Tiny Programming Interface. Но как бы страшно это не звучало, этот интерфейс поддерживается программатором USBasp, естественно он должен быть прошит последней версией по ссылке выше. Подключение:
Программирование поддерживается при питании 5В, так что не забудьте переключить программатор в этот режим.
ПО я писал и заливал в среде Arduino IDE. Для самых маленьких аттини есть ядро. Правда про библиотеки думаю можно забыть, тут только хардкор поместится.
Возможные траблы
Сначала я собрал схему на макетке, чтобы отладить прошивку. Разъема для программирования на плате я не предусмотрел, да и дисплей наверное не пережил бы питания в 5В. Тут из проблем встречалось только то что где-то после 20й прошивки начались с этой самой прошивкой проблемы, которая прошивалась только на 2-3-4 раз. Вероятно это связано с моим железом.
После сборки платы вроде все заработало, но при втором включении я уже увидел шум на дисплее вместо данных. В первую очередь я заменил всю обвязку дисплея, но это не помогло. Программные ухищрения тоже. Еще раз прогуглив варианты обвязки- заметил что часто встречается аппаратная задержка на RESET дисплея. Ее добавление и решило проблему.
Схема
По больше части схема повторяет прошлую. Тут только добавлен С2 и защита от превышения напряжений. Так если по питанию придет больше 12В, на которые рассчитан MCP1703- некоторый диапазон отработает параметрический стабилизатор R3 D3, а при дальнейшем превышении R3 сгорит как предохранитель. Так-же в линию АЦП добавлен стабилитрон D1.
Делитель с плечом 3.5 позволяет измерить до 3.5*3.3=11.55В. При раздельном питании и измеряемом напряжении диапазон получается 0-11.55В. Если объединить питание и вход диапазон 4-12В. Это обусловлено тем что опорное напряжение ATTINY10 берет равным линии питания, которая здесь 3.3В плюс падение на стабилизаторе. Итого шаг на значение АЦП 11.55/255=0.045В.
Плата
Плата так-же сделана под возможности ЛУТ, и по большей части повторяет прошлую. Тут контакты для проводов находятся под дисплеем, а для торчащего шлейфа добавлены пара миллиметров справа.
Дисплей просто приклеил на двусторонний скотч.
Программа
ПО по большей части позаимствовано здесь и здесь. Итого ПО занимает всего 912 байт.
Код
uint8_t AD;
uint16_t VOLT;
const uint8_t Init[24] = {
0xAE, // Display OFF
0xA8, 0x1F, // set multiplex (HEIGHT-1): 0x1F for 128x32, 0x3F for 128x64
0x22, 0x00, 0x03, // Page min to max
0x20, 0x01, // Memory addressing mode 0x00 Horizontal 0x01 Vertical
0xDA, 0x02, // Set COM Pins hardware configuration to sequential
0x8D, 0x14, // Charge pump enabled
0xD3, 0x00, // Display offset to 0
0x81, 0xFF, // Set contrast
0xD9, 0xF1, // Set pre-charge period
0xDB, 0x40, // Set vcom detect
0x21, 0x00, 0x7F, // Column min to max
0xAF, // Display on
};
#define PI2C_SDA PB0
#define PI2C_SCL PB1
#define OUT_REG PORTB
#define SDA_ON (OUT_REG |= (1<< PI2C_SDA))
#define SDA_OFF (OUT_REG &= ~(1<< PI2C_SDA))
#define SCL_ON (OUT_REG |= (1<< PI2C_SCL))
#define SCL_OFF (OUT_REG &= ~(1<< PI2C_SCL))
#define SDA_READ (PINB & (1<<PI2C_SDA))
#define ADDR 0b01111000 //OLED Address plus write bit
inline void dly() { //пустая команда
__asm__("NOP");
};
void setup () {
ADMUX = 2 << MUX0; // ADC1 (PB1)
ADCSRA = 1 << ADEN | 3 << ADPS0; // Enable ADC, 125kHz clock
DDRB = 3;
for (uint8_t i = 0; i < 100; i++) dly();
start();
Tx(ADDR);
Tx(0x00);
for (uint8_t i = 0; i < 24; i++)
{
Tx(Init[i]);
}
stop();
}
void loop(void) {
uint8_t buffer[8] = {0, 0, 10, 0, 0, 11, 12, 12}; //знаковый буффер на 8 ячеек.
ADCSRA = ADCSRA | 1 << ADSC; // Start
while (ADCSRA & 1 << ADSC); // Wait while conversion in progress
AD = ADCL; //Читаем АЦП
VOLT = (AD *47)/10; //Преобразуем значение АЦП в вольты
buffer[0] = VOLT / 1000; //первый знак
buffer[1] = (VOLT % 1000) / 100; //второй знак
buffer[3] = (VOLT % 100) / 10; //третий знак
buffer[4] = VOLT % 10; //четвертый знак
OLED_printB(buffer); //Выводим буффер
}
Шрифт и отрисовка
const uint8_t OLED_FONT[] PROGMEM = {
0x7F, 0x41, 0x7F, // 0 0
0x00, 0x00, 0x7F, // 1 1
0x79, 0x49, 0x4F, // 2 2
0x41, 0x49, 0x7F, // 3 3
0x0F, 0x08, 0x7E, // 4 4
0x4F, 0x49, 0x79, // 5 5
0x7F, 0x49, 0x79, // 6 6
0x03, 0x01, 0x7F, // 7 7
0x7F, 0x49, 0x7F, // 8 8
0x4F, 0x49, 0x7F, // 9 9
0x00, 0x60, 0x00, // . 10
0x1F, 0x78, 0x1F, // V 11
0x00, 0x00, 0x00, // - 12
};
void OLED_printB(uint8_t *buffer) {
start();
Tx(ADDR);
Tx(0x40);
for (uint8_t i = 0; i < 8; i++) OLED_printD(buffer[i]); // print buffer
stop(); // stop transmission
}
uint8_t OLED_stretch(uint8_t b) {
b = ((b & 2) << 3) | (b & 1); // split 2 LSB into the nibbles
b |= b << 1; // double the bits
b |= b << 2; // double them again = 4 times
return b; // return the value
}
void OLED_printD(uint8_t ch) {
uint8_t i, j, k, b; // loop variables
uint8_t sb[4]; // stretched character bytes
ch += ch << 1; // calculate position of character in font array
for (i = 8; i; i--) Tx(0x00); // print spacing between characters
for (i = 3; i; i--) { // font has 3 bytes per character
b = OLED_FONT[ch++]; // read character byte
for (j = 0; j < 4; j++, b >>= 2) sb[j] = OLED_stretch(b); // stretch 4 times
j = 4; if (i == 2) j = 6; // calculate x-stretch value
while (j--) { // write several times (x-direction)
for (k = 0; k < 4; k++) Tx(sb[k]); // the 4 stretched bytes (y-direction)
}
}
}
Работа с I2C
/* i2c start sequence */
void start() {
SDA_ON;
dly();
SCL_ON;
dly();
SDA_OFF;
dly();
SCL_OFF;
dly();
}
/* i2c stop sequence */
void stop() {
SDA_OFF;
dly();
SCL_ON;
dly();
SDA_ON;
dly();
}
/* Transmit 8 bit data to slave */
bool Tx(uint8_t dat) {
for (uint8_t i = 0; i < 8; i++) {
(dat & 0x80) ? SDA_ON : SDA_OFF;
dat <<= 1;
dly();
SCL_ON;
dly();
SCL_OFF;
}
SDA_ON;
SCL_ON;
dly();
bool ack = !SDA_READ; // Acknowledge bit
SCL_OFF;
return ack;
}
Файлы
Файлы на гитхаб https://github.com/ENGIN33RRR/Attiny10_VoltMeter
Схема и ПП в диптрейс, прошивка в Arduino IDE.
Автор: Владислав