USB программатор для AVR и интерфейс обмена данными с ПК в одном флаконе
Речь пойдет о том как получить USB программатор для AVR и интерфейс обмена данными с ПК в одном флаконе
Что понадобится из железа:
- Микросхема серии FT2232 - http://www.ftdichip.com/Products/FT2232C.htm
- Микроконтроллер AVR с SPI интерфейсом - http://atmel.ru/Production/tables/avr.htm
Схему можно собрать согласно автору AVReal (см. рисунок) - http://real.kiev.ua/old/avreal/ru/adapters.html
Что понадобится из ПО:
- Программу AVReal - http://real.kiev.ua/avreal
- Драйвер FTD2XX - http://ftdichip.com/Drivers/D2XX.htm
- И компилятор С++.
Проверить что устройство собрано и работает правильно можно выполнив следующую команду с использованием AVReal:
avreal32.exe +mega324p -pd="DLP2232M A",s="FTPOH771A" -aft2232:reset=adbus3:enable=adbus4 -r test.hex >test.txt
Пример приведен для FT2232D в связке с Atmega324p. Подробности по командам программирования здесь: http://real.kiev.ua/old/avreal/ru/description.html
Код под микроконтроллер может быть следующим (что бы далеко за примером не ходить - взял из 3DO DiagBlastera под Atmega324p):
#include <avr/io.h>
#include <avr/interrupt.h>
typedef unsigned char uint8;
//входной и выходной кольцевые буферы
volatile uint8 dbi_ring[256];
volatile uint8 dbo_ring[256];
volatile uint8 dbo_down, dbo_up, dbi_down, dbi_up;
void SPI_SlaveInit(void)
{
/* Set MISO output, all others input */
DDRB = (1<<PB6);
/* Разрешаем работу SPI и прерывание по окнчанию передачи байта */
SPCR = (1<<SPE)|(1<<DORD)|(1<<SPIE);//|(1<<SPR0); // режим 0, LSB - см. Atmega324p Datasheet С. 168
SPDR=0; //обнуляем регистр данных
}
ISR(SPI_STC_vect) //прерывание по получению байта
{
uint8 tmp=SPDR; //читаем данные
//отправляем данние если есть, если нет - можно просто отправить любое значени
//в данном примере подразумевается текстовый протокол, поэтому ноль используется как незначащий символ
if( dbo_down==dbo_up ) //исходящий буфер пуст
SPDR=0;
else //есть что отправить
SPDR=dbo_ring[dbo_down++];
if(!tmp)return; //ничего не получили - выход
dbi_ring[dbi_up++]=tmp; //получили - запись в кольцевой буфер для обработки в главной процедуре
}
void Debug_Init()
{
dbo_down=dbo_up=0; //обнулим выходной кольцевой буфер
dbi_down=dbi_up=0; //обнулим входной кольцевой буфер
SPI_SlaveInit(); //инициализация ведомого SPI интерфейса
}
int main() // пример главной процедуры
{
Debug_Init(); //инициализация SPI интерфейса
DDRA = 0xff; // на порт А можно повесить светодиоды для проверки
PORTA = 0;
sei(); //разрешаем прерывания
while(1) //бесконечный цикл
{
//если пришли данные выведем их на порт А
if(dbi_down!=dbi_up) PORTA = dbi_ring[dbi_down++];
}
return 0;
}
Для компиляции примера необходимо использовать AVR Studio и WinAVR.
Код со стороны ПК привожу в виде простейшего и потому надеюсь понятного примера:
#include <windows.h>
#include "FTD2XX.h"
#pragma comment(lib, "FTD2XX.lib")
//Класс для работы с AVR через FT2232 в режиме 0, LSB - см. Atmega164p datasheet С. 168
class SPIDeb
{
private:
FT_HANDLE ftHandle; //хендл устройства - надо отметить, что тип FTDI устройства в данном примере не проверяется, но это не сложно добавить )
FT_STATUS ftStatus; //переменная для статуса исполнения команд
bool opened; //признак что устройство открыто
public:
SPIDeb(){opened=false;}; //конструктор
~SPIDeb(){Close();}; //деструктор
bool Init(unsigned int divval); //подключение устройства, divval - делитель для получения частоты = 12М/((divval+1)*2)
int Send(const unsigned char *buff, int size); //отправка буфера по SPI
int Read(unsigned char *buff, int size); //чтение принятых от устройства данных
void Close(); //отключение устройства
};
bool SPIDeb::Init(unsigned int divval)
{
DWORD sended=0; //переменная в которую пишется число отправленных байт
char sendbuff[]={0x86,0x2,0x00};
sendbuff[1]=divval&0xff; //запишем в команду установки делителя частоты переданное значение
sendbuff[2]=divval>>8; //с помощью данной команды можно регулировать частоту SPI обмена
if(opened)return true;
ftStatus = FT_Open(0,&ftHandle); //открываем первое попавшееся устройство
if (ftStatus != FT_OK) return false; //если ничего не открылось - информируем об ошибке
UCHAR Mask = 0x13; // здесь указывается направление сигнала (1 - выход, 0 - вход)
//соответствие сигналам FT2232: bit0(AD0)...bit7(AD7) - относительно контроллера будет: SCK,MOSI,MISO,RESET,CS.....
UCHAR Mode = 2; // режим MPSSE см. док. FT_000109
ftStatus = FT_SetBitMode(ftHandle,Mask,Mode); //установка режима
FT_Write (ftHandle, (void*)sendbuff, 3, &sended); //отправим устройству команду установки частоты
opened=true;
if (ftStatus == FT_OK) return opened;
//если режим MPSSE открытым устройством не поддерживается - сворачиваем деятельность
Close();
return false;
}
int SPIDeb::Send(const unsigned char *buff, int size)
{
DWORD sended=0; //переменная в которую пишется число отправленных байт
unsigned char *sbf; //буфер для отправки данных
if(!opened)return 0;
sbf=new unsigned char[size*12]; //выделяем память под поток команд для устройства
//поскольку контроллеру нужно время на обработку принятого байта и извлечение из исходящего буфера данных для отправки
//использовались четыре команды на каждый обмен байтами с устройством
for(int i=0;i<size;i++) //формирование массива команд для FT2232 см. док. FT_000109 с сайта производителя
{
sbf[i*12]=0x80; //выбор SPI устройства - установка CS в низкий уровень
sbf[i*12+1]=0x02;
sbf[i*12+2]=0x13;
sbf[i*12+3]=0x3f; //посылка байта в контроллер с одновременным чтением, полученный байт затем можно считать функцией Read
sbf[i*12+4]=0x07;
sbf[i*12+5]=buff[i];
sbf[i*12+6]=0x80; //отключение SPI устройства - CS в высокий уровень
sbf[i*12+7]=0x12;
sbf[i*12+8]=0x13;
sbf[i*12+9]=0x1b; //фиктивная запись в устройство - для задержки - что бы контроллер успел приготовиться к следующему обмену
sbf[i*12+10]=0x07;
sbf[i*12+11]=buff[i];
}
FT_Write (ftHandle, (void*)sbf, 12*size, &sended); //запись блока команд в устройство
delete []sbf; //чистим память - конечно лучше выделить фиксированный буфер, но на скорую руку и так сойдет
return sended/12; //вернем число переданных байт
}
int SPIDeb::Read(unsigned char *buff, int size)
{
DWORD inquery=0,readed=0;
if(!opened)return 0;
FT_GetQueueStatus(ftHandle,&inquery); //проверка - есть ли данные в очереди?
if(inquery>size)inquery=size; //если данных больше чем размер буфера - примем столько сколько сможем
if(!inquery)return 0; //нет данных - на выход
FT_Read(ftHandle,buff,inquery,&readed); //чтение
return readed; //возврат количества полученных байт
}
void SPIDeb::Close()
{
if(!opened)return;
FT_SetBitMode(ftHandle,0,0); //сбрасываем устройство
FT_Close(&ftHandle); //отключаем драйвер
}
вт, 15/07/2014 - 10:29
Проект эмуляции