
Сначала была идея
На работе (работаю в медицине) пыхтят и мучаются два электромеханических самописца ну и я вместе с ними: тушь, механика – знаете ли неприятно. Как то раз в мою голову забрела идея: а почему бы не заменить два задыхающихся от старости самописца простой системой сбора данных состоящей из контроллера АЦП и ПК (пусть даже слабенького и старенького) с соответствующим программным обеспечением с возможностью вывода на принтер? И тут понеслось в голове и закружилось…
Лирика
Так как кроме идеи у меня ничего не имелось: ни опыта, ни особых познаний в схемотехнике и электронике, пришлось перелопатить изрядное количество литературы и пошарить по просторам сети. Также проект предполагался бюджетным, и поэтому пришлось освоить метод ЛУТ, научиться травить платы, сверлить и паять – о чем нисколько не жалею. Таким образом я прошел некоторый, пусть небольшой, путь — путь проб и ошибок, привожу некоторые наблюдения, сделанные мной в процессе, может, кто-то сравнит их со своими и согласится, а может и не согласится:
- быстрого старта, как такого, не получится, не стоит строить иллюзии — придется попотеть;
- эмуляторы типа 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 — хочется освоить новое железо.
Полезная литература
Помогут переварить все вышенаписанное книги:
П. Хоровиц, У. Хилл. “Искусство схемотехники” — классическая вещь;
Патрик Гелль. Как превратить персональный компьютер в измерительный комплекс — упоминалась выше.