Как связать ПК с контроллером ATmega

Ранее я делал схему для связи любого ПК с МК. Теперь рассмотрим как все это выглядит со стороны контроллера и со стороны ПК.

Сторона контроллера

2013-03-29 22.36.32

Для контроллера это простой UART. Когда компьютер посылает какие-либо данные контроллер вызывает прерывание и дальше мы обрабатываемым эти данные. Для этого лучше написать 2 простые функции.

void USART_Init(){
	//UCSRA=(1<<U2X);
	UCSR0B=(1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0)|(1<<TXB80)|(1<<RXB80);
	//UCSRC=(1<<UCPOL);
	UBRR0H=0;//
	UBRR0L=1;//bound
	//TXD/PD1 - Output
	DDRD |= (1<<PD1);
}
void transmitUART(unsigned char data_){
	while( !(UCSR0A&(1<<UDRE0)) );
	UDR0 = data_;
}

И все остальное можно обрабатывать в прерывании примерно так

ISR(USART_RX_vect){
	transmitUART(UDR0);
	switch(usartBuffer){
		case 0x31:
			if(n==0){
				PORTB |= (1<<PB1);
				PORTB &= ~(1<<PB2);
				n=1;
			}else{
				PORTB |= (1<<PB2);
				PORTB &= ~(1<<PB1);
				n=0;
			}
		break;
		default:
			// DEFAULT OP
		break;
	}
}

Что-бы общение было хоть как-то осмысленным принимаем что первый принятый байт посылки - команда, а дальше действуем по желаемой модели. Например команда 0×31 ( ASCII = 1 ) будет переключать светодиоды на контроллере.

Вот и все, теперь можно переходить к ПО для ПК.

Сторона ПК

Снимок3

Тут я выбираю, наверное, самое простое решение. Опен соурс SharpDevelop, очень удобен и все необходимое включено, при том без танцев с бубном.

И так, C# это совсем не тоже самое что С/С++, но все же Си.

Нам нужна будет библиотека работающая с периферией, называется она так

using System.IO.Ports;

Дальше создается экземпляр класса, настраиваем его и можно отправлять посылки.

public static SerialPort serialPort1 = new SerialPort();
serialPort1.PortName = "COM4"; //Указываем наш порт
serialPort1.BaudRate = 256000;//9600; //указываем скорость.
serialPort1.DataBits = 8; 
serialPort1.Open(); //Открываем порт.

//Событие вызовется когда получены данные
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler_);

//Посылаем контроллеру информацию
serialPort1.WriteLine("1");//0x31

Всяческие приблуды в виде оконного приложения и обработки нажатия на кнопку не буду рассматривать.

 

PS: Подытожу. На ПК запускается приложение в котором описана логика взаимодействия пользователя с периферийным устройством, это приложение посылает/принимает информацию от устройства.

Прошивка совместимая с AVRStudio

Немного неудобно, когда пишешь код в одной программе а заливаешь его через другую. По этому случаю, совершенно случайно, нашел прошивку под схему PROTTOSS`а совместимую со студией.

Собственно вот ссылка на форум. Так что основная моя претензия к программатору не актуальна.

 

Как дойдут руки перепрошить, выложу результаты.

 

Для общего развития картинка Фрактала ( Множество Мандельброта )

PS: Фрактал – это не просто красотулечька, при их помощи сжимают информацию, убирают шумы и еще много интересного. 

640px-Mandelpart2

Фотоаппарат своими руками

Сразу начну с исходника в котором показывается, как получить изображение с мышки.

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

Как только, об этом узнал, принялся сразу разбирать все мышки в доме, но не все имеют сенсор способный выдавать дамп пикселей. Долгие поиски, показали, что такие сенсоры есть только в мышах Logitech. И тут мне повезло, у друга завалялась старя мыша. Разобрав ее, я увидел желанный ADNS. Вот по нему документация.

2013-03-23 13.27.13 В доке даже есть примеры схемы, одна из которых и есть эта мыш. Так что, проблем с пониманием чего к чему нет.

А что делать дальше?

Для начала надо продумать как все это будет работать. 

Сервопривод HXT 900 с ATmega 8

Решил я попробовать как-то раз подключить сервопривод к контроллеру. Дело с такими вещами я раньше не имел, так что купил первую попавшуюся машинку.

Далее пошел копать на нее мануал, но оказалось это дело непростое. Сам производитель поскупился, и написал всего лишь 1 листочек. Hextronik_HXT900_datasheet. Поиски по разным форумам помогли найти мне электрическую схему.

Servo HXT900

 HXT900 с ATmega8 (не пугаться, на макете много лишнего)

 Приведу некоторые важные параметры

  • Напряжение питания: 4.8 – 6 В (но говорят можно и от 9В питать, правда дольше он от этого не прослужит, на деле питал от 3.3В все ОК)
  • Потребляемый ток: Очень долго искал, и только на одном форуме между строк нашел 700 мА, кажется мне, это враки, ибо от USB (500 мА), он работает у меня прекрасно. Источник на изи элктроникс говорит, что до 100 мА. Лично я намерил 180 мА максимум
  • Крутящий момент: 1.5 кг см2
  • Угол поворота: 90 градусов

Теперь о том как им управлять.

  • Коричневый провод – земля
  • Красный – питание
  • Желтый – сигнальный (должно быть не меньше 3.3 В)

Везде говорят, что это простой ШИМ, но верить им не стоит. Несущая частота 50 Гц (20 мс), и положение вала задается шириной импульса. От 0.45 мс ( 2222.2[2] Гц ) до 2.45 мс ( ~408.16 Гц ).

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

  1. Если частота например 1 Гц, то он 1 раз в секунду будет поворачивать вал на небольшой угол, и так пока не придет к заданному значению.
  2. Если частота будет 200 Гц, то отклик и жесткость выросте (когда на сигнальной линии пусто, то ротор двигателя можно спокойно вращать), но так же упадет крутящий момент.

servo_pulse

 

Примерно так выглядит сигнальная линия

С точки зрения программирования, эта задача очень простая. И даже не стоит того, чтобы занимать аппаратный ШИМ. Так как в приложениях с этим приводом скорее всего он будет не один, а ШИМ один, думаем как сделать все ручками.

Проще всего сделать это с помощью таймера.

В теле задаем делитель и запускаем таймер, так же разрешаем прерывание. (у меня 12 МГц кристалл, делитель не установлен)

sei(); 
TCCR0 |= (0<<CS02)|(1<<CS00); 
TIMSK |= (1<<TOIE0);

и в прерывании уже доводим до частоты. (данный код раз в секунду двигает вал в другое крайнее положение)

ISR (TIMER0_OVF_vect) {
 if(i>j)//(24) 47 - 94 (118) // через столько переполнений пройдет 0.45 и 2.45 мс соответственно, в скобках дествительные
 PORTB &=0;
 if(i>938){//50Гц - это 938 пререполнений таймера при частоте 12 МГц
 PORTB |=(1<<PB1)|(1<<PB2)|(1<<PB0); // 2 диода и серво
 i=0;
 }
 if(bo>46875){ // 1 секунда // каждую секунду меняет задающее значение ротора.
 if(j==24)
 j=118;
  else
 j=24;
 bo=0;
 }
 bo++;
 i++;
}

Можно все это хорошенько формализовать и сделать хоть для каждой ноги, и на каждую прицепить сервопривод.

Напоследок, видео

Полезные ссылки:

Миниатюрный программатор AVR

Отличный программатор, на мой взгляд.

Вот ссылка на него.

Самый маленький из тех что видел.

Особенно радует совместимость с XMega т.к. это MKII клон.

В общем будем копировать=)

Флэшка своими руками

Привет всем. Для создания своей флэшки с блэк джеком и шлюхами я взял модуль памяти M25P32 (документация), контроллер ATMega168 (документация) и конвертер USB<->UART (SerialPort) (документация).

И так. Зачем все это нужно?

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

Схема

Без имени-2

Это документация на используемые схемы. Там же и есть способы их подключения.

FT232RL_V202 m25p32 atmega168

 

 

 

 

 

 

По сути же схема выглядит так

 

Протоколы

Здесь мы используем 2 основных протокола. Это UART и SPI оба они реализованы в контроллере, остается только привести их в удобный для себя вид.

Терминал

 

Основные ошибки с UARTом:

  • Забыть линию TXD настроить выходом ( например DDRD |= (1<<PD1) )
  • Скорость передачи лучше брать из стандартного ряда а значения для UBBR из мануала

Для проверки я использовал вот эту прогу (Terminal)

Не забывать при SPI:

  • Задать все порты (входы выходы)
  • Давать задержку между передачей и принятием данных

Программа

Для своего удобства разделил программу на несколько блоков

Вообще-то этот проект сделан чтобы автоматически прибавлять числа и записывать в память, но от изначальной задачи он отличается совсем немного.

Все изменения будут заключены в одном единственном блоке.

Пусть у нас будет 3 комманды:

  1. Запись данных
  2. Считывание данных
  3. Стирание

Изменения будут только в блоке прерывания:

ISR(USART_RX_vect){
 if(command==0xFF){
  command = UDR0;
 }else{
  switch(command){
   case 0x01://Запись данных

   break;
   case 0x02://Чтение данных

   break;
   case 0x03://Стирание данных

   break;
  }
 }
}

Проект

SoftWare

Наверное отдельно рассмотрю как делать ПО для такого типа приложений в другой статье. Но в общем и целом эта программа должна подключаться к одному из COM портов и передавать определенные пакеты данных.

HardWare

Это плата на которой это все тестилось.

2013-02-13 11.40.52

 

Питание так же от USB.

Заключение

Самое лучшее применение этой идеи, я думаю в дистанционных приборах, которые остаются где-либо и записывают что-то. Потом, с помощью специальной программы данные считываются и обрабатываются.

 

SPI на других ногах

Как работать с встроенным в AVR SPI`ем будет описано в другой статье. Иногда возникает необходимость синхронного общения по этому протоколу с двумя устройствами (в моем случае это внешний АЦП и внешняя память).

atmega88_p2

И так, для всего этого берем любой контроллер AVR (я взял ATmega168) и определяемся где мы хотим разместить наш ручной SPI. Займет это все 3-4 порта, зависит от необходимости в канале SS. Для моих целей  SS не нужен, т.к. он выбран всегда.

Шаг 1: Определяем порты и частоту

В моем случае:

  • PC3 – выход
  • PC2 – вход
  • PC1 – тактирование

Чтобы не мучиться сразу зададим все дефайнами:

#define MO0 PORTC&=~(1<<PC3)
#define MO1 PORTC|=(1<<PC3) 
#define SCK 8
#define C SCK/2
#define SCK_ON PORTC|=(1<<PC1)
#define SCK_OFF PORTC&=~(1<<PC1)

Соответственно: 1,2  - строки определяют 1/0 на выходе; 5,6 – 1/0 на линии такта; SCK – количество тактов за один период SPI,  если частота кристалла 8МГц, а SPI 2МГц, то это число будет 4. Да и надо помнить, что меньше 2 оно не может быть.

Шаг 2: Определяем главную последовательность

   // Поднимаем такт для режима 4 (для 0 - опускаем линию такта)
   for(bit=0; bit<8;bit++){ // Далее передаем 8 бит данных
     //передаем первый бит входных данных (byte)
     // опускаем линию такта (для режима 0 - поднимаем)
     // считываем бит и записываем в выходной регистр
     // поднимаем линию такта (для режима 0 - опускаем)
   }

Таким образом у нас получится такая вот картина

1234921860_15347_FT18372_zilog_spi_sck_

 

Шаг 3: Расставляем по своим местам

unsigned char SPI (unsigned char byte){
   unsigned char re=0;
   unsigned char bit=0;
   SCK_ON;
   for(bit=0; bit<8;bit++){
      //write
      if (byte&(1<<bit))
         MO1;
      else
         MO0;
      //SCK on
      wait(C);
      SCK_OFF;
      //Read
      re<<=1;
      if(PINC&(1<<PC2))
         re|=(1<<0);//0x01
      else
         re&=~(1<<0);
      // SCK off
      wait(C);
      SCK_ON;
    }
}

 

Готовый проект My SPI под AVRStudio 6