Как стать автором
Обновить

Простой осциллограф за 1 день

Время на прочтение7 мин
Количество просмотров179K
Здрасьте!
Ну нет у меня денег на нормальный осциллограф(и на нормальную камеру). Так что сильно не деритесь.
Но было 500 рублей на экран и простой 8-битный микроконтроллер.

Небольшое описание под катом.


Началось все с того, что на руках у меня появился графический LCD экранчик WG12864B фирмы WINSTAR. 128x64 пикселя, монохромный. Никогда с ними не работал, было интересно в нем разобраться (люблю копаться в даташитах, особенно, по Cortex M4 контроллерам в 1400 страниц).

Управление оказалось очень простым, мне показалось проще, чем управление символьными LCD экранами. Основная первоначальная сложность возникла лишь в том что экран поделен на 2 независимые части 64x64, каждая из которых управляется собственным контроллером. Об управлении писать не буду, в интернете тьма статей разных и библиотек.

Дак вот. Вывел я картинку, порадовался, показал маме. Что же делать дальше, с экраном вроде разобрался, дальше картинки рисовать уже скучно. Решил сделать осциллограф, потому что у меня его нету, и, наверное, еще долго не будет. И тут сразу вспомнилась одна функция этого экрана «Стартовая линия дисплея». Она служит, так сказать, смещением памяти. Если записать в память пиксель в точке (0,0) и сделать стартовую линию, например 5, то точка будет видна на экране на 5 линии горизонтально. Мне показалось это решением проблемы сдвига изображения вместо его перерисовки.

Решил что одну половину экрана я буду сдвигать вместе с изображением сигнала, а на второй будет показываться различная информация: напряжение и все такое.
Логика проста. Стираем строчку(старую точку которая хочет вылезти справа при сдвиге более 64 точек), строим точку, эквивалентную напряжению, сдвигаем на 1 пиксель влево изображение. Эффект соединенных точек сначала думал сделать через алгоритм Брезенхема, но потом подумал, что сдвигаем ведь всего на 1 пиксель и линии будут вертикальные.

Амплитудное значение определяется по формуле:
amp=63-(8-битное значение из АЦП) сдвинутое на 2 разряда вправо;

Сделал все это в бесконечном цикле, запустил, и… ничего не увидел. добавил задержку 100мс и получил то изображение, которое видно в видео. При уменьшении задержки изображение становится слаборазличимым. Тут я взгрустнул, так как сигналы частотой выше 10 Гц становятся совсем неразличимы. Это все из за метода сдвига экрана. Если стирать экран и записывать информацию блоками, а не пикселями, как это делал я, качество изображения значительно улучшится и ускорится отрисовка. Но делать это было, честно, неохота, особенно в сессию. И я оставил все как есть.

На нижней части экрана я нарисовал шкалу времени, подписал отрезок. Шкалу напряжения пришлось делать на нижнем экране, так как на сдвигающемся экране сложновато сделать несдвигающуюся шкалу, тем более, чтобы её было нормально видно.

Вот картинка, чтоб удобней было ориентироваться:
image

На текущий момент имеем 3 Вольта. Кому интересно, синус делал на ЦАПе МК Cortex M4 с периодом чуть больше 3 секунд. На экране видно, что пару значений не досчитал на пике.

Воот. А место-то еще осталось и добавил я еще пару цифровых каналов. Работают они на прерываниях и чуть пошустрей аналогового, так как немного по другому принципу: отобразили сигнал слева-направо, стерли, отобразили снова. Тут уж можно просмотреть цифровой сигнал с частотой меньше 50 Герц. Например, проверить работоспособность какого-нибудь медленного интерфейса. Очень медленного:)

В общем все рассказал. Предложения и отзывы в комментариях. Только не разводите балаган, лучше купите мне осциллограф)
Счастья всем.

UPDATE1: Спасибо всем за критику, после сессии попытаюсь более серьезно отнестись к программе и аппаратной части, опираясь на ваши замечания.

UPDATE2: вот код для atmel studio для микроконтроллера ATMega168, который содержит процедуры работы с графическим экраном, обработку прерываний таймера и цифровых входов.
Просмотреть код
#include <avr/io.h>
#define F_CPU 8000000
#include <util/delay.h>
#include <avr/interrupt.h>

#define ClearBit(reg, bit) reg &= (~(1<<(bit)))
#define SetBit(reg, bit) reg |= (1<<(bit))
#define nCS1 0
#define nCS2 1
#define NRST 4
#define DI   5
#define E    4
#define RW   5
#define clrcs PORTD|=(1<<nCS1)|(1<<nCS2)
#define setcs1 PORTD&=~(1<<nCS1)
#define setcs2 PORTD&=~(1<<nCS2)
#define sete PORTD|=(1<<E)
#define clre PORTD&=~(1<<E)
#define setdi PORTC|=(1<<DI)
#define clrdi PORTC&=~(1<<DI)
#define setrst PORTC&=~(1<<NRST)
#define clrrst PORTC|=(1<<NRST)
#define setrw PORTD|=(1<<RW) 
#define clrrw PORTD&=~(1<<RW)

void sendbyte(char b)
{		
	PORTB=b;
	sete;
	_delay_us(1);
	clre;
	_delay_us(5);
}

void SetXY(int x,int y,int c)
{
	char h=PIND;
	if (c&0x1) setcs1;
	if (c&0x2) setcs2;
	clrdi;
	sendbyte(0xB8+x);
	sendbyte(0X40+y);
	PORTD=h;
}

void initglcd()
{
		_delay_ms(50);
		clrdi;
		clrcs;
		setrst;
		_delay_us(10);
		clrrst;
		
		setcs1;
		setcs2;
		sendbyte(0xC0);
		sendbyte(0x3F);
		SetXY(0,0,3);
}

void sendi(char b,char c)
{
	if (c&0x1) setcs1;
	if (c&0x2) setcs2;
	setdi;
	sendbyte(b);
	clrdi;
	clrcs;
}

unsigned char readbyte(char c) 
{
	setdi;
	setrw;
	if (c&0x1) setcs1;
	if (c&0x2) setcs2;
	DDRB=0x00;
	_delay_us(1);
	sete;
	_delay_us(1);
	clre;
	_delay_us(5);
	sete;
	_delay_us(1);
	unsigned char x=PINB;
	
	clre;
	
	DDRB=0xff;
	clrcs;
	clrdi;
	clrrw;
	
	return x;
}

void clearglcd(char c)
{     
	 for(int j=0; j<=7;j++){
	 SetXY(j,0,3);
	 for(int i=0;i<=63;i++)sendi(0x00,c); }
	 SetXY(0,0,3);
}

void fillglcd(char c)
{
	for(int j=0; j<=7;j++){
		SetXY(j,0,3);
	for(int i=0;i<=63;i++)sendi(0xff,c); }
	SetXY(0,0,3);
}

void putpixel(char x, char y)
{  cli();
	if (x<64)
	{
		SetXY(y/8,x,1);
		unsigned char t=readbyte(1);
		SetXY(y/8,x,1);
		sendi(t|(1<<(y%8)),1);
	}
	else
	{
		SetXY(y/8,x-64,2);
		unsigned char t=readbyte(2);
		SetXY(y/8,x-64,2);
		sendi(t|(1<<(y%8)),2);
	}
	sei();
}

void clearpixel(char x, char y)
{  cli();
	if (x<64)
	{
		SetXY(y/8,x,1);
		unsigned char t=readbyte(1);
		SetXY(y/8,x,1);
		sendi(t&(~(1<<(y%8))),1);
	}
	else
	{
		SetXY(y/8,x-64,2);
		unsigned char t=readbyte(2);
		SetXY(y/8,x-64,2);
		sendi(t&~((1<<(y%8))),2);
	}
	sei();
}

int ff=63;
int hh=0;
int asd=0;
int asd1=0;
int pred=0;
int pred1=0;
int oldd=63;
int oldd1=63;

ISR(TIMER0_OVF_vect)
{   
	hh++;
	if (hh==2) 
	{
	hh=0;
	if (ff==0) 
	{   ff=63; 
		oldd=63;
		oldd1=63;
		for(int k=0;k<=63;k++) clearpixel(100,k);
		for(int k=0;k<=63;k++) clearpixel(101,k);
		for(int k=0;k<=63;k++) clearpixel(102,k);
		for(int k=0;k<=63;k++) clearpixel(103,k);
		for(int k=0;k<=63;k++) clearpixel(104,k);
		for(int k=0;k<=63;k++) clearpixel(105,k); 
		
		for(int k=0;k<=63;k++) clearpixel(115,k);
		for(int k=0;k<=63;k++) clearpixel(116,k);
		for(int k=0;k<=63;k++) clearpixel(117,k);
		for(int k=0;k<=63;k++) clearpixel(118,k);
		for(int k=0;k<=63;k++) clearpixel(119,k);
		for(int k=0;k<=63;k++) clearpixel(120,k);
	} else ff--;
	if (asd==1) 
	{   
		if (pred==0)
		{
			if (ff<oldd)for(int k=oldd;k>=ff;k--) putpixel(100,k); 
			putpixel(101,ff);
			putpixel(102,ff);
			putpixel(103,ff);
			putpixel(104,ff);
			putpixel(105,ff);
			pred=asd;
			oldd=ff;
		}
		
		
	} else
	{   
		if (pred==1)
		{   if (ff<oldd) for(int k=oldd;k>=ff;k--) putpixel(105,k); 
			putpixel(101,ff);
			putpixel(102,ff);
			putpixel(103,ff);
			putpixel(104,ff);
			putpixel(100,ff);
			pred=asd;
			oldd=ff;
		}
	}
	
	
	if (asd1==1)
	{
		if (pred1==0)
		{
			if (ff<oldd1)for(int k=oldd1;k>=ff;k--) putpixel(115,k);
			putpixel(116,ff);
			putpixel(117,ff);
			putpixel(118,ff);
			putpixel(119,ff);
			putpixel(120,ff);
			pred1=asd1;
			oldd1=ff;
		}	
	} else
	{
		if (pred1==1)
		{   if (ff<oldd1) for(int k=oldd1;k>=ff;k--) putpixel(120,k);
			putpixel(116,ff);
			putpixel(117,ff);
			putpixel(118,ff);
			putpixel(119,ff);
			putpixel(115,ff);
			pred1=asd1;
			oldd1=ff;
		}	
	}
}

}	

ISR(INT0_vect)
{ 
	if (PIND&0x04) 
	{
		asd=1;
	}
	else 
	{
		asd=0;
	}	
}

ISR(INT1_vect)
{
	if (PIND&0x08)
	{
		asd1=1;
	}
	else
	{
		asd1=0;
	}
}

int main(void)
{   
	DDRC=0x3C;
	DDRD=0x33;
	DDRB=0xff;
	PORTD=0x00;
	initglcd();
	clearglcd(1);
	
	for (int j=0;j<=127;j++)
	for(int i=0;i<=63;i++)
	putpixel(j,i);
	for (int j=0;j<=127;j++)
	for(int i=0;i<=63;i++)
	clearpixel(j,i);
	
	ADCSRA|=(1<<ADEN)|(0<<ADIE)|(1<<ADSC)|(1<<ADATE)|(1<<ADPS0);
	ADMUX=0x60;
	
	for(int k=0;k<=63;k++) putpixel(66,k); //OX
	putpixel(65,56);
	putpixel(67,56);
	putpixel(65,46);
	putpixel(67,46);
	putpixel(65,36);
	putpixel(67,36);
	putpixel(65,26);
	putpixel(67,26);
	putpixel(65,16);
	putpixel(67,16);
	putpixel(65,6);
	putpixel(67,6);
	
	for(int k=6;k<=56;k++) putpixel(84,k); //OY
	putpixel(83,56);
	putpixel(85,56);
	putpixel(83,46);
	putpixel(85,46);
	putpixel(83,36);
	putpixel(85,36);
	putpixel(83,26);
	putpixel(85,26);
	putpixel(83,16);
	putpixel(85,16);
	putpixel(83,6);
	putpixel(85,6);
	
	putpixel(87,56); //0
	putpixel(88,57);
	putpixel(88,55);
	putpixel(89,57);
	putpixel(89,55);
	putpixel(90,57);
	putpixel(90,55);
	putpixel(91,56);
	
	putpixel(87,46); //1 
	putpixel(88,46);
	putpixel(89,46);
	putpixel(90,46);
	putpixel(91,46);
	putpixel(88,47);
	
	putpixel(87,36); //2
	putpixel(87,37);
	putpixel(87,35);
	putpixel(88,35);
	putpixel(89,35);
	putpixel(89,36);
	putpixel(89,37);
	putpixel(90,37);
	putpixel(91,37);
	putpixel(91,36);
	putpixel(91,35);
	
	putpixel(87,27); //3
	putpixel(87,26);
	putpixel(87,25);
	putpixel(88,25);
	putpixel(89,25);
	putpixel(89,26);
	putpixel(89,27);
	putpixel(90,25);
	putpixel(91,25);
	putpixel(91,26);
	putpixel(91,27);
	
	putpixel(87,17); //4
	putpixel(87,15);
	putpixel(88,17);
	putpixel(88,15);
	putpixel(89,17);
	putpixel(89,15);
	putpixel(89,16);
	putpixel(90,15);
	putpixel(91,15);
	
	putpixel(87,6); //5
	putpixel(87,7);
	putpixel(87,5);
	putpixel(88,7);
	putpixel(89,5);
	putpixel(89,6);
	putpixel(89,7);
	putpixel(90,5);
	putpixel(91,7);
	putpixel(91,6);
	putpixel(91,5);
		
	putpixel(68,53); //1
	putpixel(69,53);
	putpixel(70,53);
	putpixel(71,53);
	putpixel(72,53);
	putpixel(69,54);
	
	putpixel(68,48); //s
	putpixel(68,50); 
	putpixel(68,49);
	putpixel(69,51);
	putpixel(70,50);
	putpixel(70,49);
	putpixel(71,48);
	putpixel(72,49);
	putpixel(72,50);
	putpixel(72,51);
	
	for(int k=0;k<=63;k++) putpixel(95,k); 
	for(int k=0;k<=63;k++) putpixel(94,k);
	for(int k=0;k<=63;k++) putpixel(111,k);
	putpixel(110,35);
	putpixel(112,35);
	putpixel(110,63);
	putpixel(112,63);
	putpixel(110,7);
	putpixel(112,7);
	
	EICRA=0x05;
	EIMSK=0x03;
	TIMSK0=0x01;
	TCCR0B=0x04;
	sei();
	
   	unsigned char i=0;
	int x=0; int y=0;
	bool b=0;
	unsigned char old,news,news2;
	unsigned char adcc;
	old=63;
	while(1){
		for(int k=0;k<=63;k++) clearpixel(k,y);
		for(int k=56;k>=6;k--) clearpixel(80,k);
		for(int k=56;k>=6;k--) clearpixel(81,k);
		adcc=ADCH;
		news=63-adcc*64/255;
		news2=adcc*50/255;
		for(int k=56;k>=56-news2;k--) putpixel(80,k);
		for(int k=56;k>=56-news2;k--) putpixel(81,k);
		if (news<=old) for(int h=old;h>=news;h--) putpixel(h,y); else for(int h=old;h<=news;h++) putpixel(h,y); 
		old=news;
		y--;
		if (++x==4) {PORTC^=0x08; x=0;}
		if (x%2==1) PORTC^=0x04;
		if (y<0) y=63;
		setcs1;
		sendbyte(0xC0+i);
		if (i==0) i=63; else i--;
		_delay_ms(97);
		
	}
}
Теги:
Хабы:
Всего голосов 80: ↑60 и ↓20+40
Комментарии45

Публикации

Истории

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань