Перейти к содержанию
    

Управление мегой от ПК (через SPI)

Как управлять ATMega с компьютера, средствами SPI ?

 

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

 

Мега, получает с ПК управляющий код (байт), анализирует его и выполняет какое то определенное действие (пускай будет моргать светодиодами на порту). Используется SPI, мега получается в режиме salve.

 

Схема к данной задачи:

mega_pc_139.gif

 

После моих проб, ошибок и советов добрых людей получилось 2 реализации:

* Программирую в CodeVisionAVR

 

1. С использованием примера из даташита atmega16 (стр. 132)

 

#include <mega16.h>

#define DDR_SPI DDRB
#define DD_MISO PORTB.6


// Управляющие коды, которые могут быть переданы
#define CMD1 0x01
#define CMD2 0x02
#define CMD3 0x03
#define CMD4 0x04

void SPI_SlaveInit(void)
{
/* Set MISO output, all others input */
DDR_SPI = (1 << DD_MISO);
/* Enable SPI */
SPCR = (1 << SPE);
}

unsigned char SPI_SlaveReceive(void)
{
/* Wait for reception complete */
while(!(SPSR & (1 << SPIF)))
;
/* Return data register */
return SPDR;
}

void main (void){
SPI_SlaveInit();
DDRA = 0xFF; // сделали порт A выходом


while (1){

// анализируем полученный ответ
switch(SPI_SlaveReceive())
{
case CMD1:
PORTA = 0x01;
break;
case CMD2:
PORTA = 0x01;
break;
case CMD3:
PORTA = 0x03;
break;
case CMD4:
PORTA = 0x04;
break;
default:
PORTA = 0xFF;
break;
}

};
}

 

2. С использованием стандартных функций в spi.h (а также codewizard'а в cvavr):

 

#include <mega16.h>
#include <spi.h>


// Управляющие коды, которые могут быть переданы
#define CMD1 0x01
#define CMD2 0x02
#define CMD3 0x03
#define CMD4 0x04

unsigned char read;

void main (void){

// инициализируем порты
PORTB=0x00;
DDRB=0x40; //тут SPI порт
PORTF=0x00;
DDRF=0xFF;

// инициализируем SPI
SPCR=0x40;
SPSR=0x00;

while (1){ 

read = spi(0x00);
/* [b]вот здесь мне непонятно что писать в качестве аргумента spi()? [без аргумента выдает ошибку]. [/b]*/

switch(read)
{
    case CMD1:
        PORTA = 0x01;
        break;
    case CMD2:
        PORTA = 0x02;
        break;
    case CMD3:
        PORTA = 0x03;
        break;
    case CMD4:
        PORTA = 0x04;
        break;
    default:
        PORTA = 0xFF;
        break;
}
         };
}

 

Проблема в том, что оба этих варианта не работают, и цели поставленной задачи достичь не удалось :cry:

 

Прошу помочь, натолкнуть в сторону где можно найти ошибку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

(стёр, ибо поиском наешл сообщения автора и посмотрел что у него со стороны компа за софт)

 

--

 

Дежавю! Еще тема: http://electronix.ru/forum/index.php?showtopic=55022

 

На SS должен подаваться низкий уровень, чтобы SPI работал.

Изменено пользователем SysRq

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С компа, самописной программкой. В ней не сомневаюсь (я выдернул кусочек, из когда то написанного программатора для той же меги). Вся загвоздка имхо в прошивке контроллера.

 

*если требуется код проги выложу

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В ней не сомневаюсь.

Частоту оценить не забудьте: длительность низкого\высокого уровня клоков должна быть не менее 2 тактов МК.

 

Вся загвоздка имхо в прошивке контроллера.

Может быть и в аппаратуре ;) Если AVCC не подключен к VCC, порт А работать не будет. Про SS я выше написал (отердактировал, м.б. не заметили).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

последовательно сигналам - резисторы Ом по 100, для согласования уровней и SS на GND (mega8 без SS не работает точно, не знаю как 16-я).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проблема в том, что оба этих варианта не работают, и цели поставленной задачи достичь не удалось

Обратите внимание, как вы выталкиваете данные из лпт и как заталкиваете в ведомое устройство, возможно, проблема в этом. Сделайте симметричную команду. Если не сработает, то попробуйте просто принять любой байт и вывести его в портА на светодиоды, сразу будет видно, принимаете вы данные в принципе или нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если AVCC не подключен к VCC, порт А работать не будет.

Are you sure? В смысле - вы это точно знаете?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Are you sure?

 

ATmega16 datasheet Rev. 2466R-06/08 says:

AVCC is the supply voltage pin for Port A and the A/D Converter. It should be externally connected

to VCC, even if the ADC is not used. If the ADC is used, it should be connected to VCC

through a low-pass filter.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

попробуйте просто принять любой байт и вывести его в портА на светодиоды

 

Так и делал PORTA = SPDR;

На светодиоды выводит передаваемы байт. Т.е передача работает. Однако, не удается обработать полученный байт и уже оперировать им.

 

Код наподобе этого, и вариации, что я перепробывал - отказываются работать:

 

while (1){

 

// анализируем полученный ответ
switch(SPI_SlaveReceive())
{
case CMD1:
PORTA = 0x01;
break;
case CMD2:
PORTA = 0x01;
break;
case CMD3:
PORTA = 0x03;
break;
case CMD4:
PORTA = 0x04;
break;
default:
PORTA = 0xFF;
break;
}

 

Так же не работает и попытка счиатать значение SPDR в переменную =(

 

unsigned char read;
read = SPDR;

 

Подскажите, в чем здесь может быть заковырка?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что сделал бут ч/з SPI на базе ByteBlaster.

Если надо, то могу процедуры нижнего уровня открыть. И про подводные камни рассказать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Отписал Вам в личное сообщение, но не уверен, что оно дошло. Был бы очень благодарен, если бы поделились вашим опытом и информацией

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дело в том, что mega работает в режиме slaveSPI. В этом режиме должна использоваться нога SS. Это надо не забывать. По ней осуществляется синхронизация. Опыт показывает, что достаточно синхронизировать пакет, а не каждый байт. Тем не менее...

 

У меня, как я и писал, связь осуществляется посредством ByteBlaster-а. Того же, который используется при программировании в AvReal. Я им пользуюсь, как и многие (спасибо Real-у). В данном адаптере не используется нога SS. В связи с этим ей надо управлять каким то другим макаром. У меня к этой ноге есть доступ непосредственно у самой меги. Чем я и пользуюсь. В противном случае надо реализовать хотя бы заземление её.

 

У меня SPI используется несколько не стандартно. Я его использую полудуплексно. То есть посылаю пакет меге, а потом получаю пакет ответа. В связи с некоторыми особенностями схемы, а также благодаря моей программной ошибке у меня иногда происходила рассинхронизация между пакетами. Я ввёл дополнительную синхронизацию, дабы обеспечить устойчивую работу. В последствии, когда ошибка была устранена, я не стал убирать синхронизацию, так как убедился в её эффективности. Сейчас стабильность 100% за счёт контроля и повторов.

 

Разрабатывалась в связи с тем, что требовалось задействовать в изделии ногу RESET. После соответственного прошивания фуза, ISP программирование перестаёт функционировать. Для того, чтобы можно было изделие перешивать, и был написан данный бутлоадер.

 

Это было краткое описание. Теперь сами процедуры.

 

На AVR.

// Сбросить SPI
void ClrSPI(void)
{
  PORTB = 0xff;                                            // Передёрнуть SS
  PORTB = 0xfc;                                            // Обнулить 
  SPSR;                                                    // сбросить флаги
  SPDR;         
}

Используется после каждого исполнения команды. То есть после цикла приём-передача. У вас может и не быть. Но должен быть SS на земле.

// Ждать прихода символа по SPI
void WaitSPSR(void)
{
  while(!(SPSR & (1<<SPIF)))                             // ждать освобождения буфера
  {
     __watchdog_reset();                                // сбросить собаку
     if((_TIFR1 &(1<<TOV1))!=0)                            // Если пришло время моргнуть
     {
       _TIFR1 = 1<<TOV1;                                // перезарядить таймер
       BLINK;                                            // и моргнуть
     }
  }
}

То что вам не надо - типа моргания светодиодом - можете выкинуть. Просто беру "как есть", чтобы исключить ошибки.

// Вывод символа на SPI
void TxByte(uint8_t    outbyte)                            // передача
{
  SPDR = outbyte;                                        // вывести
  WaitSPSR();                                              // ждать Завершения операции
  SPDR;
}

 

Ввод делаю прямо в тексте проги типа так...

....
      SPDR=1;                                            // Для синхронизации
      WaitSPSR();                                          // ждать прихода символа
      c=SPDR;                                            // Читаем символ
....

Обратите внимание на "1".

Для самого приёма, естественно, без разницы что туда передаётся. А вот на IBM, я по этой "1" ловлю синхронизацию.

 

 

Теперь IBM.

Я использовал Delfi7 и SmallPort. Сам SmallPort я уже где-то выкладывал, но если надо, то прикреплю. Работает очень устойчиво и весит мало. Идёт под XP и 98/Me. Под другими просто не проверял. В вызовах вы легко разберётесь и просто переделаете под себя.

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

 

Procedure WaitMks;
var
  i  : integer;
  a  : integer;
begin
  for i:=0 to FTime do
    a:=a+Form1.SmallPort.Port[$378];
end;

function TxRx(data:byte):byte;
var
  i     : integer;
  a,b   : byte;
begin
b:=0;
for i:=0 to 7 do begin
   b := b shl 1;                                // следующий бит
   a := (data shr 1) and $40;                   // текущий бит
   Form1.SmallPort.Port[$378]:= a;              // выдать в MOSI
   WaitMks;
   Form1.SmallPort.Port[$378]:= a or 1;         // стробировать (SCK=1)
   WaitMks;
   if((Form1.SmallPort.Port[$379] and $80)=0)
   then b := b or  1;                           // принять новый бит
   WaitMks;
   Form1.SmallPort.Port[$378]:= a;              // стробировать (SCK=0)
   data := data shl 1;                          // следующий бит
end;
result := b;
end;

 

Покажу ещё две процедуры - приём/передача пакета. Разобрав их поймёте как осуществлялась синхронизация.

procedure WriteMes;
var
    i    : integer;
    a,
    data : byte;
begin
  for i:=0 to 31 do begin
    data := TxRx(SPACE);
    if data = 1 then break;
    a := SPACE and $40;                                 // текущий бит
    Form1.SmallPort.Port[$378]:= a;                     // выдать в MOSI
    WaitMks;
    Form1.SmallPort.Port[$378]:= a or 1;                // стробировать (SCK=1)
    WaitMks;
    Form1.SmallPort.Port[$378]:= a;                     // стробировать (SCK=0)
  end;
  Mes.crc8 := Mes.Address;                              // инициализировать CRC
  TxRx(FEND);                                           // Передаём символ FEND
  Mes.crc8 := calcCRC(Mes.crc8,FEND);                    // Подсчитать CRC8
  TxRx(Mes.Address or $80);                             // Передаём ADRWAKE
  Mes.crc8 := calcCRC(Mes.crc8,Mes.Address);            // Подсчитать CRC8
  TxRx(Mes.Command);                                    // Передаём команду
  Mes.crc8 := calcCRC(Mes.crc8,Mes.Command);            // Подсчитать CRC8
  OutByteWake(Mes.DataSize);                            // Передаём длину
  for i:=0 to Mes.DataSize-1 do
        OutByteWake(Mes.Data[i]);                       // Передаём данные
  OutByteWake(Mes.crc8);                                // Передаём CRC
end;

procedure ReadMes;
label mend;
var
    i   : integer;
    d,a : byte;
    err : boolean;
    nerr: integer;
begin
  Mes.Err := 0;                                         // Пока нет ошибок
  Mes.crc8 := Mes.Address;                              // инициализировать CRC
  nerr := 0;
  d:=0;
  repeat
    d := d shl 1;                                       // следующий бит
    a := SPACE and $40;                                 // текущий бит
    Form1.SmallPort.Port[$378]:= a;                     // выдать в MOSI
    WaitMks;
    Form1.SmallPort.Port[$378]:= a or 1;                // стробировать (SCK=1)
    WaitMks;
    if((Form1.SmallPort.Port[$379] and $80)=0)
    then d := d or  1;                                  // принять новый бит
    Form1.SmallPort.Port[$378]:= a;                     // стробировать (SCK=0)
    WaitMks;
    nerr := nerr+1;
    if(nerr>800)then begin
       Mes.Err := 11;                                    // Нет ответа
       goto mend;
    end;
  until(d=FEND);                                        // Ждём начала пакета
  Mes.crc8 := calcCRC(Mes.crc8,FEND);                    // Подсчитать CRC8
  d := TxRx(SPACE) and $7f;                             // Читаем адрес
  Mes.crc8 := calcCRC(Mes.crc8,d);                      // Подсчитать CRC8
  if(d<>Mes.Address)then begin
    Mes.Err := 6;                                       // Ошибка адреса
    goto mend;
  end;
  d := TxRx(SPACE);                                     // Читаем команду
  Mes.crc8 := calcCRC(Mes.crc8,d);                      // Подсчитать CRC8
  if((d and $80)<>0)then begin
    Mes.Err := 10;                                      // Ошибка команды
    goto mend;
  end;
  Mes.Command := d;
  d := GetByteWake;                                     // Читаем длину
  Mes.DataSize := d;
  for i:=0 to Mes.DataSize-1 do
        Mes.Data[i] := GetByteWake;                     // Читаем данные
  d := GetByteWake;                                     // Читаем CRC
  if(Mes.crc8<>0)then begin
    Mes.Err := 9;                                       // Ошибка CRC
  end;
  mend:
end;

 

В завершение прошу здесь опустить разбор моего стиля программирования. Я не выкладываю эти тексты, как образец стилистики. Цель - помочь. Пусть пользуется тот, кому это нужно. Писалось на скорую руку за 2 ночи (переделывалось), поэтому прошу извинение за стилистику написания.

 

PS: Совсем забыл. Прошу отметить что у меня откинута нога RESET. В противном случае надо соответствующий бит в процедурах установить в "1".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я, конечно, мегу никогда не кодил, но как-то странно....

Может тип не тот...Или там оптимизатор выделывается

Может добавить volatile к read....

 

Там какая-то очень простая ошибка должна быть по идее

 

Это я в ответ на

Так же не работает и попытка счиатать значение SPDR в переменную =(

unsigned char read;

read = SPDR;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

инициализация

void spi_init(void)
{
  volatile unsigned char tmp;
  spi_state = SPI_STATE_IDLE;
  spi_count = 0;
  DDRB |= _BV(PB4);        //MISO as output
  DDRB &= ~(_BV(PB2) | _BV(PB3) | _BV(PB5));    //MOSI, SCK, SS as input
  PORTB |= (_BV(PB2) | _BV(PB3) | _BV(PB5));    //enable pullup MOSI, SCK, SS
  SPCR = _BV(SPE) | _BV(SPIE);        //enable SPI & interrupt
  tmp = SPDR;
  tmp = SPSR;
  SPDR = 0;
}

прерывание

ISR(SPI_STC_vect)
{
  unsigned char data;
  data = SPDR;            //read data register
  if(spi_state == SPI_STATE_IDLE)
  {
    switch(data)
    {
      case SPI_CMD_RX:
        spi_state = SPI_STATE_RX;
        SPDR = 0x1;
        break;
      case SPI_CMD_TX:
        spi_state = SPI_STATE_TX;
        break;
....
    }
  }
  else
  {
    switch(spi_state)
    {
...
    }
  }
}

 

на компе часть кода от avrdude - ppiwin.c, ppi.h

и функция битбанг

#define get_MISO() (ppi_get(&fd, PPISTATUS, 0x40))
#define set_MOSI() (ppi_set(&fd, PPIDATA, 0x20))
#define clear_MOSI() (ppi_clr(&fd, PPIDATA, 0x20))
#define set_SCK() (ppi_set(&fd, PPIDATA, 0x40))
#define clear_SCK() (ppi_clr(&fd, PPIDATA, 0x40))
#define set_rst() (ppi_clr(&fd, PPIDATA, 0x10))
#define clear_rst() (ppi_set(&fd, PPIDATA, 0x10))

unsigned char SPI_rxtx_byte(unsigned char data)
{
  unsigned char rx = 0;
  char i;
  
  for(i=0; i<8; i++) // loop for the 8 data bits
  {
    // send one bit
    if(data & 0x80) set_MOSI(); 
    else clear_MOSI();
    data <<= 1; // shift next bit in place

    set_SCK();  // SCK high
    usleep(SPI_SCK_DELAY);

    // receive one bit
    rx <<= 1; // shift next bit in place
    if(get_MISO())
    {
      rx |= 1;
    }
    clear_SCK();  // SCK lOW
    usleep(SPI_SCK_DELAY);
  }
  return rx;
}

 

 

 

повторюсь, без согласования входов по уровню (похоже КМОП защелкивались) - на двух компах из трех связь сбоила и не работала как надо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...