Конвертер уровней RS232-TTL на ST232 для связи микроконтроллера с компьютером
При отладке самодельного GPS приемника приходилось выводить данные из пакетов GGA и VTG на ЖКИ, что при максимальном размере пакета в 80 символов не очень удоб
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пФ).
Шина I2C и AVR пример, PCF8583 (RTC) вечный календарь
Шина I2C очень распространена, существуют АЦП, ЦАП, термометры, память с интерфейсом I2C. При проектировании устройств и их починке приходится сталкиваться с этой шиной. Рассмотрим ее поподробнее.
- <<в начало
- ‹ предыдущая
- …
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- …
- дальше >
- в конец >>