atmega8

warning: Creating default object from empty value in /var/www/fendercustomsh/data/www/avrlab.com/modules/taxonomy/taxonomy.pages.inc on line 34.

Анимация на 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:
фото GPS приемника
фото GPS приемника
А так выглядит внешняя активная антенна ANT GPS 66801-00 HFL 8CM для него:
фото антенны GPS приемника
Теперь на пальцах про систему GPS. Система GPS представляет собой сеть спутников околоземной орбите, каждый спутник посылает на землю сигнал о своем местоположении и точном времени отправления сигнала, GPS приемник получает эти сигналы и по задержкам сигналов от спутников определяет свое положение. Для этого ему нужно минимум 3 сигнала с разных спутников. Для точного определения координат и высоты над уровнем моря уже нужно 4 спутника. Далее эти сигналы декодируются в координаты, которые посылаются в формате NMEA (в некоторых приемниках есть еще собственный формат вывода координат) к обрабатывающему микроконтроллеру, который выводит эти данные на дисплей. Учитывая прошлые проблемы с подсоединением GPS модуля и его питанием, решил вытравить для него специальную макетную плату, где будет размещаться сам модуль и стабилизатор LP2980-3.3 на 3.3В с обвязкой. Также для согласования уровней, на входа GPS модуля были подключены к делителям напряжения. Вот так это выглядит по завершению:
фото макетки 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, которая будет определять выводимые данные на дисплей. Теперь рассмотрим собственно код прошивки:

  1. #include <avr/io.h>
  2. #include <avr/interrupt.h>
  3.  
  4. #define RS 2 //RS=PD1
  5. #define E 3 //E=PD3
  6.  
  7. #define TIME 10 //Time constant for LCD delay
  8. //Tick with 4Mhz
  9.  
  10. unsigned char mode=0;
  11.  
  12. unsigned char gga_count=0;
  13. unsigned char vtg_count=0;
  14.  
  15. unsigned char gga_data[90]={};
  16. unsigned char vtg_data[90]={};
  17.  
  18. void pause (unsigned int a)
  19. {
  20. unsigned int i;
  21. for (i=a;i>0;i--);
  22. }
  23.  
  24. void lcd_com (unsigned char lcd)//Write command to LCD
  25. {
  26. unsigned char temp;
  27. temp=(lcd&~(1<<RS))|(1<<E); //RS=0 - it's command
  28. PORTD=temp; //Write higth tetrade, RS, E to portA
  29. asm("nop"); //Settle pause
  30. PORTD=temp&~(1<<E); //Write higth tetrade to LCD
  31. temp=((lcd*16)&~(1<<RS))|(1<<E); //RS=0 - it's command
  32. PORTD=temp; //Write low tetrade, RS, E to portA
  33. asm("nop"); //Settle pause
  34. PORTD=temp&~(1<<E); //Write low tetrade to LCD
  35. pause (10*TIME); //Pause for executing command
  36. }
  37.  
  38. void lcd_dat (unsigned char lcd)//Write data to LCD
  39. {
  40. unsigned char temp;
  41. temp=(lcd|(1<<RS))|(1<<E); //RS=1 - it's data
  42. PORTD=temp; //Write higth tetrade, RS, E to portA
  43. asm("nop"); //Settle pause
  44. PORTD=temp&~(1<<E); //Write higth tetrade to LCD
  45. temp=((lcd*16)|(1<<RS))|(1<<E); //RS=1 - it's data
  46. PORTD=temp; //Write low tetrade, RS, E to portA
  47. asm("nop"); //Settle pause
  48. PORTD=temp&~(1<<E); //Write low tetrade to LCD
  49. pause(TIME); //Pause for loading data
  50. }
  51.  
  52. void lcd_init (void) //Init LCD
  53. {
  54. lcd_com(0x2c); //4-wire data bus, 1-row, 5x8 symbol size
  55. pause(100*TIME);
  56. lcd_com(0x0c); //Show image, cursor show
  57. pause(1000*TIME);
  58. lcd_com(0x01); //Clear screen and set cursor on 0x00 position
  59. pause (100*TIME);
  60. }
  61.  
  62. void uart_init(void)
  63. {
  64. UBRRH = 0x00; //Set 4800 bit per second
  65. UBRRL = 0x33;
  66. UCSRA = 0x00;
  67. UCSRB = (1<<RXCIE)|(1<<RXEN)|(0<<TXEN);
  68. //Receive and transmit enable, interrupt on receive complete enabled
  69. UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
  70. }
  71.  
  72. unsigned char search_gga_field (unsigned char x)//Search x-th field in GGA packet
  73. {
  74. unsigned char i, c;
  75. c=0;
  76. for (i=0;i<gga_count;i++)
  77. {
  78. if (gga_data[i]==',') c++;
  79. if (c==x) return i+1;
  80. }
  81. return 0;
  82. }
  83.  
  84. //Search x-th field in VTG packet
  85. unsigned char search_vtg_field (unsigned char x)
  86. {
  87. unsigned char i, c;
  88. c=0;
  89. for (i=0;i<vtg_count;i++)
  90. {
  91. if (vtg_data[i]==',') c++;
  92. if (c==x) return i+1;
  93. }
  94. return 0;
  95. }
  96.  
  97. void write_gps_data (unsigned char crc_valid) //Write GPS data on LCD
  98. {
  99. unsigned char i,temp;
  100. if (crc_valid==0) {
  101. //If CRC not confirm
  102. lcd_init ();
  103. lcd_dat('n');
  104. lcd_dat('o');
  105. lcd_dat(' ');
  106. lcd_dat('v');
  107. lcd_dat('a');
  108. lcd_dat('l');
  109. lcd_dat('i');
  110. lcd_dat('d');
  111. return;
  112. }
  113.  
  114. if (gga_data[search_gga_field(6)]=='0')
  115. {//If no GPS signal
  116. lcd_init();
  117. lcd_dat('n'); lcd_dat('o'); lcd_dat(' ');
  118. lcd_dat('s'); lcd_dat('i'); lcd_dat('g');
  119. lcd_dat('n'); lcd_dat('a'); lcd_dat('l');
  120.  
  121. lcd_com(0xc0);
  122. lcd_dat(gga_data[search_gga_field(7)+1]);
  123. lcd_dat(' '); lcd_dat('s'); lcd_dat('a');
  124. lcd_dat('t'); lcd_dat(' '); lcd_dat('a');
  125. lcd_dat('v'); lcd_dat('a'); lcd_dat('i');
  126. lcd_dat('l'); lcd_dat('a'); lcd_dat('b');
  127. lcd_dat('l'); lcd_dat('e');
  128. return;
  129. }
  130. if (mode==0)
  131. {//Write Latitude and Longitude
  132. lcd_init();
  133. lcd_dat('L'); lcd_dat('a'); lcd_dat('t');
  134. lcd_dat(':');
  135. temp=search_gga_field(2);
  136. for (i=temp;i<temp+9;i++)//Write Latitude
  137. {
  138. if ((i-temp)==2) lcd_dat(0xdf);
  139. lcd_dat (gga_data[i]);
  140. }
  141. lcd_dat(0xe7);
  142. lcd_dat (gga_data[search_gga_field(3)]);
  143.  
  144. lcd_com(0xc0);
  145. lcd_dat('L');
  146. lcd_dat('n');
  147. lcd_dat(':');
  148. temp=search_gga_field(4);
  149. for (i=temp;i<temp+10;i++) //Write Longitude
  150. {
  151. if ((i-temp)==3) lcd_dat(0xdf);
  152. lcd_dat (gga_data[i]);
  153. }
  154. lcd_dat (0xe7);
  155. lcd_dat (gga_data[search_gga_field(5)]);
  156. }
  157. if (mode==1) { //Write altitude and speed
  158. lcd_init();
  159. lcd_dat('A'); lcd_dat('l'); lcd_dat('t');
  160. lcd_dat('i'); lcd_dat('t'); lcd_dat('u');
  161. lcd_dat('d'); lcd_dat('e'); lcd_dat(' ');
  162. temp=search_gga_field(9);
  163. while (gga_data[temp]!=',') //Write altitude
  164. {
  165. lcd_dat(gga_data[temp]);
  166. temp++;
  167. }
  168. lcd_dat('m');
  169.  
  170. lcd_com(0xc0);
  171. lcd_dat('S'); lcd_dat('p');
  172. lcd_dat('e'); lcd_dat('e');
  173. lcd_dat('d'); lcd_dat(' ');
  174.  
  175. temp=search_vtg_field(7);
  176. while (vtg_data[temp]!=',') //Write speed
  177. {
  178. lcd_dat(vtg_data[temp]);
  179. temp++;
  180. }
  181. lcd_dat('k'); lcd_dat('m');
  182. lcd_dat('/'); lcd_dat('h');
  183. }
  184. if (mode==2) { //Write UTC time and used satellites
  185. lcd_init();
  186. lcd_dat('U'); lcd_dat('T');
  187. lcd_dat('C'); lcd_dat(' ');
  188.  
  189. temp=search_gga_field(1);
  190. for (i=temp;i<temp+6;i++) //Write UTC
  191. {
  192. if (((i-temp)==2)||((i-temp)==4) ) lcd_dat (':');
  193. lcd_dat(gga_data[i]);
  194. }
  195. lcd_com(0xc0);
  196. lcd_dat('S'); lcd_dat('a'); lcd_dat('t');
  197. lcd_dat('e'); lcd_dat('l'); lcd_dat('l');
  198. lcd_dat('i'); lcd_dat('t'); lcd_dat('e');
  199. lcd_dat('s'); lcd_dat(' ');
  200.  
  201. temp=search_gga_field(7);
  202. lcd_dat(gga_data[temp]); //Write satellites
  203. if (gga_data[temp+1]!=',') lcd_dat(gga_data[temp+1]);
  204. }
  205.  
  206. return;
  207. }
  208.  
  209. unsigned char check_crc (void) //Check CRC16
  210. { /*unsigned char i,gga_crc,vtg_crc;
  211.  
  212.  gga_crc=0x00;
  213.  
  214.  i=1;
  215.  while ((gga_data[i]!='*')&&(i<90))
  216.   {
  217.   gga_crc^=gga_data[i];
  218.   i++;
  219.   }
  220.  i++;
  221.  if (gga_data[i]!=(0x30+(gga_crc>>4))) return 0;
  222.  i++;
  223.  if (gga_data[i]!=(0x30+(gga_crc&0x0f))) return 0;
  224.  
  225.  vtg_crc=0x00;
  226.  
  227.  i=1;
  228.  while ((vtg_data[i]!='*')&&(i<90))
  229.   {
  230.   vtg_crc^=vtg_data[i];
  231.   i++;
  232.   }
  233.  i++;
  234.  if (vtg_data[i]!=(0x30+(vtg_crc>>4))) return 0;
  235.  i++;
  236.  if (vtg_data[i]!=(0x30+(vtg_crc&0x0f))) return 0; */
  237.  
  238. return 1;
  239. }
  240.  
  241. ISR (USART_RXC_vect) //Starts when first byte of NMEA message been received
  242. {
  243. gga_count=0;
  244. gga_data[gga_count]=UDR;//Get first byte to GGA packet
  245. gga_count++;
  246.  
  247. do { //Receive GGA packet
  248.  
  249. while ( !(UCSRA & (1<<RXC)) )
  250. ;
  251. gga_data[gga_count]=UDR;
  252. gga_count++;
  253.  
  254. } while ((gga_data[gga_count-1]!=0x0a)&&(gga_count<90));
  255.  
  256. vtg_count=0; //Receive VTG packet
  257. do {
  258.  
  259. while ( !(UCSRA & (1<<RXC)) )
  260. ;
  261. vtg_data[vtg_count]=UDR;
  262. vtg_count++;
  263.  
  264. } while ((vtg_data[vtg_count-1]!=0x0a)&&(vtg_count<90));
  265.  
  266. write_gps_data(check_crc()); //Write data and check CRC16
  267.  
  268. return;
  269. }
  270.  
  271. int main(void)
  272. {
  273. DDRD=0xfc; //Init PORTD as output
  274.  
  275. DDRB=0x00; //Init PORTB, PB0 as input
  276. PORTB=0x01;
  277.  
  278. pause(5000); //Settle pause
  279. lcd_init(); //Init LCD
  280.  
  281. lcd_dat('n'); lcd_dat('o'); lcd_dat(' ');
  282. lcd_dat('g'); lcd_dat('p'); lcd_dat('s');
  283. lcd_dat(' '); lcd_dat('m'); lcd_dat('o');
  284. lcd_dat('d'); lcd_dat('u'); lcd_dat('l');
  285. lcd_dat('e');
  286.  
  287. uart_init(); //Init UART
  288. sei(); //Interrupt enable
  289. while (1)
  290. if ((PINB&0x01)==0x00) { //If key pressed
  291. while ((PINB&0x01)==0x00) //Wait to unpress
  292. ;
  293. pause(1000); //Pause
  294. mode=(mode+1)%3; //Change mode
  295. }
  296.  
  297. return 1;
  298. }

В начале приема пакета срабатывает прерывание по принятию байта по 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 и выводить их на семисегментный индикатор.

  1. #include <avr/io.h>
  2. #include <avr/interrupt.h>
  3. // 0 1 2 3 4 5 6 7 8 9
  4. const unsigned char codes[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
  5.  
  6. unsigned char data[4]={0x00,0x00,0x00,0x00};
  7. unsigned char counter=0;
  8.  
  9. void pause (unsigned int a)
  10. { unsigned int i; for (i=a;i>0;i--); }
  11.  
  12. void init_timer (void)
  13. { TIMSK=(1<<TOIE0); //Enable timer overflow interrupt
  14. TCCR0=(0<<CS00)|(1<<CS01)|(0<<CS02); //Prescaller = /1
  15. }
  16.  
  17. void convert_data (unsigned int x)
  18. {unsigned int temp,res;
  19. temp=x;
  20. res=temp/1000; //Calculate 1000-s
  21. data[3]=codes[res];
  22. temp=temp-res*1000;
  23.  
  24. res=temp/100; //Calculate 100-s
  25. data[2]=codes[res];
  26. temp=temp-res*100;
  27.  
  28. res=temp/10; //Calculaate 10-s
  29. data[1]=codes[res];
  30. temp=temp-res*10;
  31.  
  32. data[0]=codes[temp]; //Calculate 1-s
  33. }
  34.  
  35. ISR (TIMER0_OVF_vect)
  36. {PORTD=0xff;
  37. PORTB=~_BV(counter); //Enable digit
  38. PORTD=~data[counter]; //Write code
  39. counter=(counter+1)%4; //Increment digit counter
  40.  
  41. TCNT0=0x00; //Clear timer
  42. }
  43.  
  44. int main(void)
  45. { unsigned int x=0;
  46. DDRD=0xff;
  47. PORTD=0x00;
  48. DDRB=0x0f;
  49. PORTB=0x0f;
  50.  
  51. pause(1000); //Settle pause
  52. init_timer(); //Init timer
  53. sei(); //Interrupt enable
  54. while(1)
  55. {convert_data(x); //Conver data to codes
  56. if (x<9999) x=x+1; //Increment data
  57. else x=0;
  58. pause(30000);
  59. }
  60. return 1;
  61. }

Код очень простой. В массиве 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пФ).

RSS-материал