
Сначала была идея
На работе (работаю в медицине) пыхтят и мучаются два электромеханических самописца ну и я вместе с ними: тушь, механика – знаете ли неприятно. Как то раз в мою голову забрела идея: а почему бы не заменить два задыхающихся от старости самописца простой системой сбора данных состоящей из контроллера АЦП и ПК (пусть даже слабенького и старенького) с соответствующим программным обеспечением с возможностью вывода на принтер? И тут понеслось в голове и закружилось…
Лирика
Так как кроме идеи у меня ничего не имелось: ни опыта, ни особых познаний в схемотехнике и электронике, пришлось перелопатить изрядное количество литературы и пошарить по просторам сети. Также проект предполагался бюджетным, и поэтому пришлось освоить метод ЛУТ, научиться травить платы, сверлить и паять – о чем нисколько не жалею. Таким образом я прошел некоторый, пусть небольшой, путь — путь проб и ошибок, привожу некоторые наблюдения, сделанные мной в процессе, может, кто-то сравнит их со своими и согласится, а может и не согласится:
- быстрого старта, как такого, не получится, не стоит строить иллюзии — придется попотеть;
- эмуляторы типа Proteus на первых порах лучше не использовать, гораздо полезнее экспериментировать на реальном железе, потрогать рукам, попробовать на вкус и на зуб. Для простейшей отладки достаточно UART;
- монтажные платы – брр, предпочитаю ЛУТ + хлорное железо+сверловка, получается чуть дольше, но дает четкое понимание того, что ты делаешь;
- советую не увлекаться монтажом smd-корпусов и «минитюаризацией» монтажных плат — это достаточно сложно для новичка, иногда бывает проще заново развести плату в «увеличенном масшабе» с простым навесным монтажом (опять же полезнее для понимания).
О теории АЦП и первой попытке
После некоторых изысканий выяснилось, что реализовать задуманную идею можно в нескольких вариантах. Простейшие решения приведены в книжке Патрика Гелля(см.подвал), там же разъясняются азы аналого-цифрового преобразования сигнала. Привожу типичную схему, взятую оттуда:

В основе схемы – микросхема АЦП. Взаимодействие с ПК реализуется на программном уровне путем формирования управляющих последовательностей импульсов на модемных линиях интерфейса RS-232 ПК. Данное обстоятельство ограничивает скорость передачи данных и увеличивает вероятность ошибки передачи. Точность измерения определяется разрядностью АЦП (в данном случае 8 разрядов), точностью подводимого к АЦП опорного напряжения (здесь используется ИОН LT 1009 CZ). Скорость оцифрования сигнала зависит от времени преобразования АЦП и опять же от точности организации интерфейса разработчиком. Собственно, данная реализация была моим первым боевым опытом (см. фото ниже), но вскоре меня разочаровала, так как чувствовал потребность в более изящном решении. Управляющая программа(драйвер) была написана под DOS на паскале.

Подобные схемы, не смотря на свою жизнеспособность, ввиду указанных недостатков практически не применяются, микросхемы АЦП обычно используются вместе с микроконтроллером, который уже организует передачу данных на аппаратном уровне, попутно выполняя какие-либо операции управления, либо примитивной обработки данных. Хорошим примером такого решения может служить вот такая схемка на основе микроконтроллера AVR:

В основе схемы лежит микроконтроллер Atmega8, микросхема сопряжения с USB FT232BM, микросхема АЦП AD7876, ИОН — REF195GP. На входе — дифференциатор на базе операционного усилителя приводит сигнал на входе к диапазону 0-5В (опорное напряжение АЦП). Аналого-цифровое преобразование сигнала осуществляет микросхема АЦП AD7876 и передает данные по интерфейсу SPI в микроконтроллер, который, в свою очередь, «выкидывает» результат в usb-порт посредством UART и микросхемы сопряжения FT232BM.
В рамках моей задачи мне нужно задействовать 4 канала АЦП. Привлекательной возможностью выглядит использование встроенного АЦП микроконтроллера с коммутируемым входом, что позволит отказаться от использования внешней микросхемы АЦП и обеспечить нужное количество каналов — дешевое и простое решение. Включать дифференциатор в схему нет необходимости, так как амплитуда сигнала на каждом из входов не превышает 2-2,5 В.
Схема моего девайса
Контроллер АЦП строится на микроконтроллере AVR Atmega8. МК обладает достаточным количеством ножек для того чтобы подключить 4 канала АЦП, сигнальные светодиоды, плату сопряжения с ПК. В схеме используется внешний ИОН L1009CZ. Плата сопряжения с ПК через интерфейс RS-232 основана на микросхеме MAX232 и обособлена от основного модуля, при необходимости может быть заменена на плату с FT232 на борту для сопряжения через USB, либо с любым bluetooth-модулем (типа BTM-222, HC-04) для коммуникации через blootooth интерфейс. На рисунке ниже представлена принципиальная схема разрабатываемого устройства:

Схема питания стандартная – на базе линейного стабилизатора 78L05, присутствует сигнальный светодиод HL1. Принципиальная схема нарисована с помощью sPlan, разводка платы — в Sprint Layout. В конце технологической цепочки — ЛУТ+травление+сверловка+пайка получаем:

Вид снизу:

Программная часть
Для кодинга использовал AVRStudio+WinAVR(AVR-GCC) под Windows, альтернатива для Linux – Eclipse + AVR-GCC. Пробовал CodeVisionAVR — неплох для начала, имеет встроенный генератор кода, ограничение по объему кода 2 кБ.
Прошивка работает по следующему алгоритму:
- инициализация: функции — UART_init, ADC_init, LED_init;
- последовательный опрос каналов в бесконечном цикле по прерыванию и передача данных через UART на com-порт ПК.
В целях отладки по тексту рассованы тестовые сообщения. Переключение канала опроса подсвечивается соответствующим светодиодом. При инициализации все 4 светодиода мигают одновременно. Для того чтобы переключение светодиодов было заметно глазу намеренно поставил задержки.
/* WinAVR version... Chip: ATmega8 Memory Model: SMALL Data Stack Size: 256 bytes Clock frequency: 4.0000 MHz PCO - PC3 - Channels */ // ATmega8 I/O register definitions #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <stdio.h> #define ADC_VREF_TYPE 0x40 #define ADC_CH1_MUX 0x1 #define ADC_CH2_MUX 0x2 #define ADC_CH3_MUX 0x3 // Counter of channel's changes uint8_t i = 0; void LED_init(void){ printf("Led initialization....\r\n"); char j = 0; DDRD|= (1<<DDD7); DDRB|= ((1<<DDB0)|(1<<DDB1)|(1<<DDB2)); for(j=0;j<5;j++) { PORTD|=(1<<PD7); PORTB|=(1<<PB0)|(1<<PB1)|(1<<PB2); _delay_ms(600); PORTB&= ~((1<<PB0)|(1<<PB1)|(1<<PB2)); PORTD&= ~(1<<PD7); _delay_ms(600); } } //Sending of single char static int my_putchar(char c, FILE *stream){ while((UCSRA&(1<<UDRE)) == 0); UDR = c; return 0; } void myUART_init(void){ // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: Off // USART Transmitter: On // USART Mode: Asynchronous // USART Baud rate: 9600 UCSRA=0x00; UCSRB=0x08; UCSRC=0x86; UBRRH=0x00; UBRRL=0x19; //Redirection of stdout static FILE mystdout = FDEV_SETUP_STREAM(my_putchar, NULL, _FDEV_SETUP_WRITE); stdout = &mystdout; printf("UART initialization....\r\n"); } void ADC_init(void){ // ADC initialization // ADC Voltage Reference: AREF pin // ADC High Speed Mode: Off // ADC Auto Trigger Source: None // Select ADC input 0 printf("ADC initialization....\r\n"); ADMUX=ADC_VREF_TYPE; ADCSRA=0x8E; } // ADC interrupt service routine ISR(ADC_vect){ // Here is sending data in RS-232 printf("I'am in interrupt...\r\n"); uint16_t data = ADCL; data+= (ADCH<<8); printf("Ch_%u=%u\r\n",i,data); //Control of ADMUX's state uint8_t pr = ADMUX; printf("ADMUX = %u\r\n",pr); data = 0; if (i == 3) { i = 0; ADMUX&= ~ADC_CH3_MUX; PORTB&= ~(1<<PB2); PORTD|=(1<<PD7); //Change of channel data = ADMUX; printf("3to0,ADMUX = %u\r\n",data); data=0; _delay_ms(25); } else { if (i == 0) { ADMUX|= ADC_CH1_MUX; PORTD&= ~(1<<PD7); PORTB|= (1<<PB0); //Change of channel data = ADMUX; printf("0to1,ADMUX = %u\r\n",data); data=0; } if (i == 1) { ADMUX&= ~ADC_CH1_MUX; ADMUX|= ADC_CH2_MUX; PORTB&= ~(1<<PB0); PORTB|= (1<<PB1); //Change of channel data = ADMUX; printf("1to2,ADMUX = %u\r\n",data); data=0; } if (i == 2) { ADMUX&= ~ADC_CH2_MUX; ADMUX|= ADC_CH3_MUX; PORTB&= ~(1<<PB1); PORTB|= (1<<PB2); //Change of channel data = ADMUX; printf("2to3,ADMUX = %u\r\n",data); data=0; } i++; _delay_ms(25); } // Start a new AD conversion ADCSRA|=0x40; }; int main(void) { myUART_init(); ADC_init(); LED_init(); printf("I'am in main...\r\n"); // Global enable interrupts sei(); // Start the first AD conversion ADCSRA|=0x40; while (1); }
После завершения отладки тестовые сообщения нужно закомментировать и убрать ненужные задержки. Код писал уткнувшись в даташит, там все подробно написано.
Программатор
Для прошивки применялся простейший программатор STK200, причем разводку платы пришлось делать самому, чтобы облегчить пайку. Прошивал avrdude. Пробовал avreal – отличная альтернатива, трудностей также не возникло.

Наконец-то – работает!
После отладки кода, нескольких циклов стирания/записи МК девайс ожил и заморгал светодиодами к моей огромной радости.
На ПК использовал текстовый терминал для работы с COM-портом. В сети их навалом, при желании можно и самому написать, например, используя QT + QSerialDevice(замечательная библиотечка для работы с com-портами, написанная нашим соотечественником Денисом Шиенковым), или как тут.
В заключение
В планах осталось только написание ПО для ПК. Вообще-то уже пишу, используя в связке QT+QSerialDevice+QWT, и скоро, скоро, чувствую, поползут мои графики по дисплею. А еще подумываю о том, чтобы реализовать аппаратную часть на МК серии STM32F, взяв за основу упоминаемую уже на хабре STM32VLDISCOVERY — хочется освоить новое железо.
Полезная литература
Помогут переварить все вышенаписанное книги:
П. Хоровиц, У. Хилл. “Искусство схемотехники” — классическая вещь;
Патрик Гелль. Как превратить персональный компьютер в измерительный комплекс — упоминалась выше.
