UART в ATtiny13 или Как вывести данные из МК за 52р

UART в ATtiny13 или Как вывести данные из МК за 52р

463
ПОДЕЛИТЬСЯ

(стоимость за 10 шт магазина Чип и Дип на момент публикации)

Когда я их брал, я твердо помнил, что у их «Даже АЦП есть, не то что таймер!» и сильно радовался их малой стоимости. Я никогда не мог удержаться от покупки различных электронных штук , и в один прекрасный момент у меня стало на 10 чрезвычайно маленьких МК больше. Я люблю ATtiny13 — дешево и сурово.
Но, когда я столкнул ATtiny13 с настоящей задачей, оказалось что одной чрезвычайно принципиальной штуки в нем нету, а конкретно, интерфейсов для передачи данных (очевидно, не считая GPIO). Ну а раз GPIO есть, то написать все что угодно можно! Поразмыслил я и пошел гуглить… И прекрасного готового решения под avr-gcc не нагуглил… О разработке (надеюсь) такового решения, данная статья — добро пожаловать под кат.

Но жииирный… На самом деле, нагуглилось приблизительно три варианта, но в одном пишут на БЭЙСИКЕ (я вообщем не знал что так можно), в другом под CVAVR (привет моему первому мигающему светодиоду) и вообщем там весь смысловой код на ужасном ассемблере, а вот 3-ий вариант вроде бы подступает… Но какой-то чрезвычайно странноватый код… Но заработал сполпинка.
Program Memory Usage: 508 bytes 49,6 % Full
Ну да хорошо, основное работает и вмещается, а там уже можно и почитать, и порефакторить… JumpStart состоялся — это основное.

Работает же. Опосля вдумчивого чтения кода становится ясно, что его создатель достоин глубочайшего уважения… Этот код похож на код человека, который программирует Чрезвычайно не так давно, а задачка то решена полностью массивная. Чрезвычайно хотелось для начинающих обрисовать несколько очевидных ошибок, но опосля третьей сообразил что сильно оффтоп, так что, как досадно бы это не звучало…

Вот, что вышло опосля не чрезвычайно долгого и не чрезвычайно вдумчивого рефакторинга: Мне довольно просто отловить Pin Change Interrupt на хоть какой ноге и выплюнуть результаты A->D преобразования. В оригинале, код занимался и приёмом и отправкой данных, но реально, моя задачка не просит приёма (тем наиболее, в главном цикле). По этому, в целях похудения, было принято решение выпилить приём.

0_о
if (count >= 8) {
PORTB |= (1<<TxD);
start = 0; temp = 0; c = 0; count = 0;
T_STOP;
OCR0A = 0;
return;
}
if(c == 1) {
if (start == 0) {
temp = 0x80;
start = 1;
count—;
}
else {
temp = data;
temp = temp >> count;
temp = temp << 7;
}
switch(temp) {
case 0x80 : PORTB &= ~(1 << TxD); break;
case 0 : PORTB |= (1 << TxD); break;
}
count++;
c = 0;
}
}

void send_ch(uint8_t data){
uint8_t f;
data = ~data;
T_START;
for(f = 0; f < 10; f++){
while(c == 0);
send(data);
}
} #define F_CPU 9600000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

uint8_t temp, count, start;
volatile uint8_t c;

#define BAUD_C 123
#define TxD PB4

#define T_START TCCR0B = (1 << CS01) // F_CPU/8
#define T_STOP TCCR0B = 0
#define T_RESET TCNT0 = 0

ISR(TIM0_COMPA_vect){
OCR0A = BAUD_C;
c = 1;
T_RESET;
}

void send(uint8_t data) { //Что вообщем такое "lov"?

Не охото много мыслить, так что, я оставил всю смысловую часть как есть, выкинул избыточное и подправил вырвиглазные штуки вроде TIMSK0=0x04. Может быть ли вообщем такое хардовое уплывание?). Быстрее всего, это не самое наилучшее, надежное и верное бла бла бла решение, но мне оно кажется чрезвычайно обычным и прекрасным! В этом коде мне сильно приглянулась реализация интервалов! Весь геморрой с высчитыванием констант для baud rate сводится к одному числу BAUD_C, которое подбирается чуток ли не экспериентально и корректируется в зависимости от некорректностей кварца (у создателя она была равна 115 и на скриншоте вроде бы достаточно точно работала.
Чтож, каковой итог?
Program Memory Usage: 304 bytes 29,7 % Full
Эта задачка уже не специфична для МК без UART и была решена не один раз. На данный момент код умеет передавать лишь один знак, а нужно передавать строчки и значения регистров. А означает, время рыться в старенькых проектах! «Фух, живём. Еще и на АЦП хватит…» Но вообщем, это еще не конец.

void send_str(char *text){
while(*text) {
send_ch(*text++);
}
}

void itoa(uint16_t n, char s[]) {
uint8_t i = 0;
do { s[i++] = n % 10 + ‘0’; }
while ((n /= 10) > 0);
s[i] = ‘’;
// Reversing
uint8_t j;
char c;
for (i = 0, j = strlen(s)-1; i<j; i++, j—) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}

void send_num(char *text, uint16_t n){
char s[6];
itoa((uint16_t)n, s);
send_str(text);
send_str(s);
}

Чрезвычайно комфортно передавать числа сходу с пояснениями, что это за числа, потому — send_num(char *text, uint16_t n).
И, с учетом примера использования:

int main(void){
DDRB |= (1 << TxD);
TIMSK0 = (1 << OCIE0A);
sei();

while(1){
_delay_ms(1);
send_num("Habr:", 4242);
}
}

Выходит
Program Memory Usage: 538 bytes 52,5 % Full
Но как полезнее! Чуток больше, чем в оригинале.

Писалась она чрезвычайно быстро, перечитывалась чрезвычайно не достаточно и может содержать чрезвычайно ошибки. Прошу не троллить — я всё исправлю. Основное, чтоб эта неувязка закончила существовать и все, кто захотит выстроить собственный датчик на ATtiny13 — имели готовое решение для интерфейса. Желаю сходу предупредить, что цели сделать прекрасную и популярную статью не было.

habrahabr.ru P.S.: Поначалу я смотрел в строну I2C и даже отыскал чрезвычайно перспективный репозиторий, но не разобрался почему Atmel Studio говорит overflow. Было бы классно и этот интерфейс запилить на t13 — может даже худее получится… Но это уже не я.

НЕТ КОММЕНТАРИЕВ

ОСТАВЬТЕ ОТВЕТ

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.