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

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

После вдумчивого чтения кода становится ясно, что его автор достоин глубокого уважения… Этот код похож на код человека, который программирует ОЧЕНЬ недавно, а задача то решена вполне мощная. Работает же. Очень хотелось для начинающих описать несколько тривиальных ошибок, но после третьей понял что сильно оффтоп, так что, увы…
В оригинале, код занимался и приёмом и отправкой данных, но реально, моя задача не требует приёма (тем более, в главном цикле). Мне достаточно просто отловить Pin Change Interrupt на любой ноге и выплюнуть результаты A->D преобразования. По этому, в целях похудения, было принято решение выпилить приём. Вот, что получилось после не очень долгого и не очень вдумчивого рефакторинга:
Не хочется много думать, так что, я оставил всю смысловую часть как есть, выкинул лишнее и подправил вырвиглазные штуки вроде TIMSK0=0x04. В этом коде мне сильно понравилась реализация интервалов! Весь геморрой с высчитыванием констант для baud rate сводится к одному числу BAUD_C, которое подбирается чуть ли не экспериентально и корректируется в зависимости от неточностей кварца (у автора она была равна 115 и на скриншоте вроде бы довольно точно работала. Возможно ли вообще такое хардовое уплывание?). Скорее всего, это не самое оптимальное, надежное и верноебла бла бла решение, но мне оно кажется очень простым и красивым!
Чтож, каков результат?
«Фух, живём. Еще и на АЦП хватит...» Но вообще, это еще не конец. Сейчас код умеет передавать только один символ, а надо передавать строки и значения регистров. А значит, время рыться в старых проектах! Эта задача уже не специфична для МК без UART и была решена неоднократно.
Очень удобно передавать числа сразу с пояснениями, что это за числа, поэтому — send_num(char *text, uint16_t n).
И, с учетом примера использования:
Получается
Чуть больше, чем в оригинале. Но насколько полезнее!

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

Я никогда не мог удержаться от покупки разных электронных штук, и однажды у меня стало на 10 очень мелких МК больше. Я люблю ATtiny13 — дешево и сердито. Когда я их покупал, я твердо помнил, что у них «Даже АЦП есть, не то что таймер!» и сильно радовался их малой цене.
Однако, когда я столкнул ATtiny13 с реальной задачей, оказалось что одной очень важной штуки в нем нету, а именно, интерфейсов для передачи данных (разумеется, не считая GPIO). Ну а если GPIO есть, то написать все что угодно можно! Подумал я и пошел гуглить… И красивого готового решения под avr-gcc не нагуглил… О создании (надеюсь) такого решения, данная статья — добро пожаловать под кат.
На самом деле, нагуглилось примерно три варианта, но в одном пишут на БЭЙСИКЕ (я вообще не знал что так можно), в другом под CVAVR (привет моему первому мигающему светодиоду) и вообще там весь смысловой код на страшном ассемблере, а вот третий вариант вроде бы подходит… Но какой-то очень странный код… Но заработал сполпинка. Но жииирный…
Program Memory Usage: 508 bytes 49,6 % Full
Ну да ладно, главное работает и вмещается, а там уже можно и почитать, и порефакторить… JumpStart состоялся — это главное.
После вдумчивого чтения кода становится ясно, что его автор достоин глубокого уважения… Этот код похож на код человека, который программирует ОЧЕНЬ недавно, а задача то решена вполне мощная. Работает же. Очень хотелось для начинающих описать несколько тривиальных ошибок, но после третьей понял что сильно оффтоп, так что, увы…
В оригинале, код занимался и приёмом и отправкой данных, но реально, моя задача не требует приёма (тем более, в главном цикле). Мне достаточно просто отловить Pin Change Interrupt на любой ноге и выплюнуть результаты A->D преобразования. По этому, в целях похудения, было принято решение выпилить приём. Вот, что получилось после не очень долгого и не очень вдумчивого рефакторинга:
#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"? 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); } }
Не хочется много думать, так что, я оставил всю смысловую часть как есть, выкинул лишнее и подправил вырвиглазные штуки вроде 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<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 — имели готовое решение для интерфейса.
P.S.: Сначала я смотрел в строну I2C и даже нашел очень многообещающий репозиторий, но не разобрался почему Atmel Studio говорит overflow. Было бы классно и этот интерфейс запилить на t13 — может даже худее получится… Но это уже не я.
P.S.S.: Мой логический анализатор. Он шикарен!
