atmega8
Анимация на NOKIA 3310 LCD данные на SD карте
Очень удачный проект использования LCD от Nokia 3310 судя по металлической рамке LCD не оригинальный. То есть это копия из Китая. Тем не менее проект рабоатет хорошо.
Как описывает автор данный проект:
SD/MMC карта памяти и микроконтроллер AVR (часть 1) Базовые операции.
Работа SD/MMC карты с микроконтроллером AVR
AVR GPS приемник на микроконтроллере ATmega8
GPS навигация все более и более проникает в массы, я не стал в этом исключением. Два года назад купил себе GPS модуль Lassen iQ от Trimble и активную антенну для него, хотел поиграться с космическими технологиями, но не вышло.GPS навигация все более и более проникает в массы, я не стал в этом исключением. Два года назад купил себе GPS модуль Lassen iQ от Trimble и активную антенну для него, хотел поиграться с космическими технологиями, но не вышло. На самом модуле был нестандартный и неудобный для соединения разъем с шагом 1,27мм, также питание было 3.3В, а у меня были макетки только для 5В питания. 3.3В было достигнуто путем подключения нескольких диодов, на которых гасилось напряжение.
В общем, мне тогда не удалось выжать никаких данных с модуля и я подумал что он паленый. Недели две назад все-таки достал его из загашника, подключил к логическому анализатору. На дисплее компьютера увидел, что модуль очень даже живой и исправно выдает данные. Обрадовавшись этому, решил сделать простенький GPS приемник на ATmega8 с выводом данных на символьный ЖКИ. Вот так выглядит GPS модуль Lassen iQ:


А так выглядит внешняя активная антенна ANT GPS 66801-00 HFL 8CM для него:

Теперь на пальцах про систему GPS. Система GPS представляет собой сеть спутников околоземной орбите, каждый спутник посылает на землю сигнал о своем местоположении и точном времени отправления сигнала, GPS приемник получает эти сигналы и по задержкам сигналов от спутников определяет свое положение. Для этого ему нужно минимум 3 сигнала с разных спутников. Для точного определения координат и высоты над уровнем моря уже нужно 4 спутника. Далее эти сигналы декодируются в координаты, которые посылаются в формате NMEA (в некоторых приемниках есть еще собственный формат вывода координат) к обрабатывающему микроконтроллеру, который выводит эти данные на дисплей. Учитывая прошлые проблемы с подсоединением GPS модуля и его питанием, решил вытравить для него специальную макетную плату, где будет размещаться сам модуль и стабилизатор LP2980-3.3 на 3.3В с обвязкой. Также для согласования уровней, на входа GPS модуля были подключены к делителям напряжения. Вот так это выглядит по завершению:

Далее макетка была присоединена к микроконтроллеру таким макаром:

Тактироваться микроконтроллер будет от внутреннего RC генератора на 4Мгц. Подтягивающие резисторы R1,R4 – 4,7кОм. R2 – ограничительный на 1кОм, R3 - на 17 Ом. Потенциометр VR1 – на 10кОм. Конденсаторы С7,С4 – электролиты на 200мкФ и 100мкФ соответственно. С4,С6,С9 – керамика на 0,1мкФ, С10 – керамика на 1мкФ, С8 – танталовый на 68мкФ.
Как видно из схемы, GPS приемник мы будем только слушать, никак не настраивая его при включении. Для этого выводы RxA, RxB подключим подтягивающими резисторами к питающему напряжению, чтобы приемник случайно не словил каких наводок и не переконфигурировался. Это важно. Теперь разберемся с форматом NMEA. Наш GPS модуль с заводскими настройками, как гласит родной даташит, на выходе TхB каждую секунду генерирует GGA и VTG сообщение, которое можно принять, настроив UART приемник на скорость 4800бит/с 8 бит данных, без проверок. Про формат GGA и VTG сообщений можно прочитать в спецификации стандарта NMEA. Оговорюсь тем, что в каждом сообщении NMEA предусмотрена контрольная сумма, обычный XOR всех байтов, не включая первый символы “$” и последний перед полем контрольной суммы “*”. Но с проверкой контрольной суммы у меня что-то не сложилось, примерно в каждом третьем пакете она не совпадает, хотя провода короткие и больших токов в схеме не проходит. Может это связано с тем, что уровни несогласованны, микроконтроллеру нужно 5В TTL, а приемник выдает 3.3В TTL. Поэтому в прошивке проверка контрольной суммы предусмотрена, но закомментирована. Как выглядит часть GGA пакета на дисплее моего логического анализатора:

Далее это сообщение расшифровывает микроконтроллер и выводит данные на экран. Расшифровать его совсем не сложно, оно представляет собой набор полей, разграниченных запятыми. Поля представлены в ASCII формате, так что заморачиваться с выводом не придется, просто нужно вывести на дисплей нужные поля. Так как на мой дисплей одновременно не помещались все данные, то была подсоединена кнопка к PB0, которая будет определять выводимые данные на дисплей. Теперь рассмотрим собственно код прошивки:
#include <avr/io.h> #include <avr/interrupt.h> #define RS 2 //RS=PD1 #define E 3 //E=PD3 #define TIME 10 //Time constant for LCD delay //Tick with 4Mhz unsigned char mode=0; unsigned char gga_count=0; unsigned char vtg_count=0; unsigned char gga_data[90]={}; unsigned char vtg_data[90]={}; void pause (unsigned int a) { unsigned int i; for (i=a;i>0;i--); } void lcd_com (unsigned char lcd)//Write command to LCD { unsigned char temp; temp=(lcd&~(1<<RS))|(1<<E); //RS=0 - it's command PORTD=temp; //Write higth tetrade, RS, E to portA asm("nop"); //Settle pause PORTD=temp&~(1<<E); //Write higth tetrade to LCD temp=((lcd*16)&~(1<<RS))|(1<<E); //RS=0 - it's command PORTD=temp; //Write low tetrade, RS, E to portA asm("nop"); //Settle pause PORTD=temp&~(1<<E); //Write low tetrade to LCD pause (10*TIME); //Pause for executing command } void lcd_dat (unsigned char lcd)//Write data to LCD { unsigned char temp; temp=(lcd|(1<<RS))|(1<<E); //RS=1 - it's data PORTD=temp; //Write higth tetrade, RS, E to portA asm("nop"); //Settle pause PORTD=temp&~(1<<E); //Write higth tetrade to LCD temp=((lcd*16)|(1<<RS))|(1<<E); //RS=1 - it's data PORTD=temp; //Write low tetrade, RS, E to portA asm("nop"); //Settle pause PORTD=temp&~(1<<E); //Write low tetrade to LCD pause(TIME); //Pause for loading data } void lcd_init (void) //Init LCD { lcd_com(0x2c); //4-wire data bus, 1-row, 5x8 symbol size pause(100*TIME); lcd_com(0x0c); //Show image, cursor show pause(1000*TIME); lcd_com(0x01); //Clear screen and set cursor on 0x00 position pause (100*TIME); } void uart_init(void) { UBRRH = 0x00; //Set 4800 bit per second UBRRL = 0x33; UCSRA = 0x00; UCSRB = (1<<RXCIE)|(1<<RXEN)|(0<<TXEN); //Receive and transmit enable, interrupt on receive complete enabled UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); } unsigned char search_gga_field (unsigned char x)//Search x-th field in GGA packet { unsigned char i, c; c=0; for (i=0;i<gga_count;i++) { if (gga_data[i]==',') c++; if (c==x) return i+1; } return 0; } //Search x-th field in VTG packet unsigned char search_vtg_field (unsigned char x) { unsigned char i, c; c=0; for (i=0;i<vtg_count;i++) { if (vtg_data[i]==',') c++; if (c==x) return i+1; } return 0; } void write_gps_data (unsigned char crc_valid) //Write GPS data on LCD { unsigned char i,temp; if (crc_valid==0) { //If CRC not confirm lcd_init (); lcd_dat('n'); lcd_dat('o'); lcd_dat(' '); lcd_dat('v'); lcd_dat('a'); lcd_dat('l'); lcd_dat('i'); lcd_dat('d'); return; } if (gga_data[search_gga_field(6)]=='0') {//If no GPS signal lcd_init(); lcd_dat('n'); lcd_dat('o'); lcd_dat(' '); lcd_dat('s'); lcd_dat('i'); lcd_dat('g'); lcd_dat('n'); lcd_dat('a'); lcd_dat('l'); lcd_com(0xc0); lcd_dat(gga_data[search_gga_field(7)+1]); lcd_dat(' '); lcd_dat('s'); lcd_dat('a'); lcd_dat('t'); lcd_dat(' '); lcd_dat('a'); lcd_dat('v'); lcd_dat('a'); lcd_dat('i'); lcd_dat('l'); lcd_dat('a'); lcd_dat('b'); lcd_dat('l'); lcd_dat('e'); return; } if (mode==0) {//Write Latitude and Longitude lcd_init(); lcd_dat('L'); lcd_dat('a'); lcd_dat('t'); lcd_dat(':'); temp=search_gga_field(2); for (i=temp;i<temp+9;i++)//Write Latitude { if ((i-temp)==2) lcd_dat(0xdf); lcd_dat (gga_data[i]); } lcd_dat(0xe7); lcd_dat (gga_data[search_gga_field(3)]); lcd_com(0xc0); lcd_dat('L'); lcd_dat('n'); lcd_dat(':'); temp=search_gga_field(4); for (i=temp;i<temp+10;i++) //Write Longitude { if ((i-temp)==3) lcd_dat(0xdf); lcd_dat (gga_data[i]); } lcd_dat (0xe7); lcd_dat (gga_data[search_gga_field(5)]); } if (mode==1) { //Write altitude and speed lcd_init(); lcd_dat('A'); lcd_dat('l'); lcd_dat('t'); lcd_dat('i'); lcd_dat('t'); lcd_dat('u'); lcd_dat('d'); lcd_dat('e'); lcd_dat(' '); temp=search_gga_field(9); while (gga_data[temp]!=',') //Write altitude { lcd_dat(gga_data[temp]); temp++; } lcd_dat('m'); lcd_com(0xc0); lcd_dat('S'); lcd_dat('p'); lcd_dat('e'); lcd_dat('e'); lcd_dat('d'); lcd_dat(' '); temp=search_vtg_field(7); while (vtg_data[temp]!=',') //Write speed { lcd_dat(vtg_data[temp]); temp++; } lcd_dat('k'); lcd_dat('m'); lcd_dat('/'); lcd_dat('h'); } if (mode==2) { //Write UTC time and used satellites lcd_init(); lcd_dat('U'); lcd_dat('T'); lcd_dat('C'); lcd_dat(' '); temp=search_gga_field(1); for (i=temp;i<temp+6;i++) //Write UTC { if (((i-temp)==2)||((i-temp)==4) ) lcd_dat (':'); lcd_dat(gga_data[i]); } lcd_com(0xc0); lcd_dat('S'); lcd_dat('a'); lcd_dat('t'); lcd_dat('e'); lcd_dat('l'); lcd_dat('l'); lcd_dat('i'); lcd_dat('t'); lcd_dat('e'); lcd_dat('s'); lcd_dat(' '); temp=search_gga_field(7); lcd_dat(gga_data[temp]); //Write satellites if (gga_data[temp+1]!=',') lcd_dat(gga_data[temp+1]); } return; } unsigned char check_crc (void) //Check CRC16 { /*unsigned char i,gga_crc,vtg_crc; gga_crc=0x00; i=1; while ((gga_data[i]!='*')&&(i<90)) { gga_crc^=gga_data[i]; i++; } i++; if (gga_data[i]!=(0x30+(gga_crc>>4))) return 0; i++; if (gga_data[i]!=(0x30+(gga_crc&0x0f))) return 0; vtg_crc=0x00; i=1; while ((vtg_data[i]!='*')&&(i<90)) { vtg_crc^=vtg_data[i]; i++; } i++; if (vtg_data[i]!=(0x30+(vtg_crc>>4))) return 0; i++; if (vtg_data[i]!=(0x30+(vtg_crc&0x0f))) return 0; */ return 1; } ISR (USART_RXC_vect) //Starts when first byte of NMEA message been received { gga_count=0; gga_data[gga_count]=UDR;//Get first byte to GGA packet gga_count++; do { //Receive GGA packet while ( !(UCSRA & (1<<RXC)) ) ; gga_data[gga_count]=UDR; gga_count++; } while ((gga_data[gga_count-1]!=0x0a)&&(gga_count<90)); vtg_count=0; //Receive VTG packet do { while ( !(UCSRA & (1<<RXC)) ) ; vtg_data[vtg_count]=UDR; vtg_count++; } while ((vtg_data[vtg_count-1]!=0x0a)&&(vtg_count<90)); write_gps_data(check_crc()); //Write data and check CRC16 return; } int main(void) { DDRD=0xfc; //Init PORTD as output DDRB=0x00; //Init PORTB, PB0 as input PORTB=0x01; pause(5000); //Settle pause lcd_init(); //Init LCD lcd_dat('n'); lcd_dat('o'); lcd_dat(' '); lcd_dat('g'); lcd_dat('p'); lcd_dat('s'); lcd_dat(' '); lcd_dat('m'); lcd_dat('o'); lcd_dat('d'); lcd_dat('u'); lcd_dat('l'); lcd_dat('e'); uart_init(); //Init UART sei(); //Interrupt enable while (1) if ((PINB&0x01)==0x00) { //If key pressed while ((PINB&0x01)==0x00) //Wait to unpress ; pause(1000); //Pause mode=(mode+1)%3; //Change mode } return 1; }
В начале приема пакета срабатывает прерывание по принятию байта по UART. Далее байты один за одним упаковываются в массивы gga_data vtg_data содержащих соответственно GGA и VTG пакет. Проверяется контрольная сумма (фейково) и выводиться на дислей. Если поле 6 в пакете GGA равно нулю, то GPS приемник не смог определить координаты и собственно пишем «no signal», если же контрольная сумма не совпадает (фейковая) пишем «no valid». Обработка же нажатия кнопки происходит в теле основной программы. Глобальная переменная mode служит для определения данных, которые будут выводиться на ЖКИ (0 – выводится широта и долгота, 1 – выводится высота и скорость, 2 – выводится точное время UTC и количество видимых приемником спутников). Переменные gga_count и vtg_count служат для определения количества принятых байт в GGA и VTG пакете.
Как работает собранный девайс у меня на подоконнике можно просмотреть на видео:
Скачать исходный код в виде проекта для AVR Studio 4 и разводку печатной платы для Sprint layout 5
Семисегментный индикатор и динамическая индикация на AVR микроконтроллере ATmega8
В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:
В разных конструкциях бывает оправдано использовать семисегментные светодиодные индикаторы, дешево и сердито по сравнению с символьными ЖКИ. Светодиодный индикатор представляет собой восемь светодиодов (7 для представления цифры и 1 для точки) расположенные в виде слегка наклоненной цифры:

Светодиоды внутри имеют общий анод (ОА) или общий катод (ОК). То есть, для управления одной цифрой нужно 8 выводов микроконтроллера. А что же делать, когда нужно управлять, например, четырьмя цифрами? Использовать микроконтроллер с 4*8=32 выводами? Не экономично.
Для такого случая придумали динамическую индикацию. Для этого соединяем выводы, которые отвечают за включение сегментов в общую шину, а общими анодами (катодами) будем управлять через транзисторы. В отдельный момент времени горит только одна цифра. Таким образом быстро перебирая цифры на дисплее (как кадры в фильме) мы получим эффект постоянно горящего изображения. В замедленном варианте, как это происходит, можно посмотреть на картинке:

А вот ускоренная в 25 раз картинка, уже начинают вырисовываться контуры «12.34»:

Используя принцип динамической индикации мы сможем управлять четырьмя цифрами при помощи 8+4=12 выводов. Использование же 2-х сдвиговых регистров HC595 может сократить это число до 4. Рассмотрим схему подключения к микроконтроллеру:

Управлять же индикатором будем с помощью микроконтроллера ATmega8. Резисторы R5-R13 – ограничительные на 470 Ом. R1-R4 – по 1кОм. Транзисторы Q1-Q4 – любые PNP типа, я использовал BC807 в планарном исполнении. Конденсаторы С5,С7 – электролиты по 100 и 200мкф соответственно, С4,С6 – керамика по 0,1мкф. Так как индикатор с общим анодом, то соответственно включение разряда/сегмента производиться низким уровнем.
Для индикаторов с общим катодом схема аналогична, только транзисторы следует взять NPN структуры, и управляться индикатор будет высоким уровнем.
Продемонстрируем сказанное, напишем программу, которая будет перебирать числа от 0 до 9999 и выводить их на семисегментный индикатор.
#include <avr/io.h> #include <avr/interrupt.h> // 0 1 2 3 4 5 6 7 8 9 const unsigned char codes[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char data[4]={0x00,0x00,0x00,0x00}; unsigned char counter=0; void pause (unsigned int a) { unsigned int i; for (i=a;i>0;i--); } void init_timer (void) { TIMSK=(1<<TOIE0); //Enable timer overflow interrupt TCCR0=(0<<CS00)|(1<<CS01)|(0<<CS02); //Prescaller = /1 } void convert_data (unsigned int x) {unsigned int temp,res; temp=x; res=temp/1000; //Calculate 1000-s data[3]=codes[res]; temp=temp-res*1000; res=temp/100; //Calculate 100-s data[2]=codes[res]; temp=temp-res*100; res=temp/10; //Calculaate 10-s data[1]=codes[res]; temp=temp-res*10; data[0]=codes[temp]; //Calculate 1-s } ISR (TIMER0_OVF_vect) {PORTD=0xff; PORTB=~_BV(counter); //Enable digit PORTD=~data[counter]; //Write code counter=(counter+1)%4; //Increment digit counter TCNT0=0x00; //Clear timer } int main(void) { unsigned int x=0; DDRD=0xff; PORTD=0x00; DDRB=0x0f; PORTB=0x0f; pause(1000); //Settle pause init_timer(); //Init timer sei(); //Interrupt enable while(1) {convert_data(x); //Conver data to codes if (x<9999) x=x+1; //Increment data else x=0; pause(30000); } return 1; }
Код очень простой. В массиве codes находятся коды, которые следует выводить на порт, чтобы получить желаемую цифру. Смена активной цифры производиться по прерыванию от переполнения таймера 0. А функция convert_data(int x) раскладывает число х по разрядам, и записывает соответствующие коды в массив data, данные из которого выводятся непосредственно на индикатор, при срабатывание прерывания.
Что из этого вышло, можно глянуть на рисунках:

Исходный код можно скачать в виде проекта под AVR Studio 4
Также, может кому пригодиться, платка для моего табло в формате .lay для программы Sprint Layout 5
Шина 1-wire, пример термометра на DS18B20 и микроконтроллере AVR, WH1602, ЖКИ
Шина 1-Wire была запатентована компанией Dallas Semiconductor, и была призвана наладить полудуплексную связь всего по одному сигнальному проводу. Также возможны варианты использования паразитного питания по линии данных (потому что во все микросхемы 1-Wire встроен конденсатор номиналом 800пФ).
- <<в начало
- ‹ предыдущая
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- дальше >
- в конец >>
