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

(цена за 10 шт магазина Чип и Дип на момент публикации)3848f9dda41340d2ae8b892325600d12.jpgЯ никогда не мог удержаться от покупки разных электронных штук, и однажды у меня стало на 10 очень мелких МК больше. Я люблю ATtiny13 — дешево и сердито. Когда я их покупал, я твердо помнил, что у них «Даже АЦП есть, не то что таймер!» и сильно радовался их малой цене.Однако, когда я столкнул ATtiny13 с реальной задачей, оказалось что одной очень важной штуки в нем нету, а именно, интерфейсов для передачи данных (разумеется, не считая GPIO). Ну, а если GPIO есть, то написать все что угодно можно! Подумал я и пошел гуглить… И красивого готового решения под avr-gcc не нагуглил… О создании (надеюсь) такого решения, данная статья — добро пожаловать под кат.На самом деле, нагуглилось примерно три варианта, но в одном пишут на БЭЙСИКЕ (я вообще не знал что так можно), в другом под CVAVR (привет моему первому мигающему светодиоду) и вообще там весь смысловой код на страшном ассемблере, а вот третий вариант вроде бы подходит… Но какой-то очень странный код… Но заработал сполпинка. Но жииирный… Program Memory Usage: 508 bytes 49,6% Full

Ну да ладно, главное работает и вмещается, а там уже можно и почитать, и порефакторить… JumpStart состоялся — это главное.bae38e6c38dc4c80b23b37e6de876829.JPG

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

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

#define F_CPU 9600000UL

#include #include #include

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»? 0_о if (count >= 8) { PORTB |= (1<> 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); } } Не хочется много думать, так что, я оставил всю смысловую часть как есть, выкинул лишнее и подправил вырвиглазные штуки вроде 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] = '\0'; // Reversing uint8_t j; char c; for (i = 0, j = strlen (s)-1; i

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

Чуть больше, чем в оригинале. Но насколько полезнее! 38936c6fa91942afb47dc16c062bf2a8.JPGХочу сразу предупредить, что цели сделать красивую и популярную статью не было. Писалась она очень быстро, перечитывалась очень мало и может содержать очень ошибки. Прошу не троллить — я всё исправлю. Главное, чтобы эта проблема перестала существовать и все, кто захочет построить свой датчик на ATtiny13 — имели готовое решение для интерфейса.

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

© Habrahabr.ru