zi4rox 0 6 апреля, 2009 Опубликовано 6 апреля, 2009 · Жалоба Как управлять ATMega с компьютера, средствами SPI ? Необходимо твердо в этом вопросе разобраться и для себя уяснить что и как. Поэтому поставил себе следующие условия простой задачи: Мега, получает с ПК управляющий код (байт), анализирует его и выполняет какое то определенное действие (пускай будет моргать светодиодами на порту). Используется SPI, мега получается в режиме salve. Схема к данной задачи: После моих проб, ошибок и советов добрых людей получилось 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: Прошу помочь, натолкнуть в сторону где можно найти ошибку. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
NullPointer 0 6 апреля, 2009 Опубликовано 6 апреля, 2009 (изменено) · Жалоба (стёр, ибо поиском наешл сообщения автора и посмотрел что у него со стороны компа за софт) -- Дежавю! Еще тема: http://electronix.ru/forum/index.php?showtopic=55022 На SS должен подаваться низкий уровень, чтобы SPI работал. Изменено 6 апреля, 2009 пользователем SysRq Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zi4rox 0 6 апреля, 2009 Опубликовано 6 апреля, 2009 · Жалоба С компа, самописной программкой. В ней не сомневаюсь (я выдернул кусочек, из когда то написанного программатора для той же меги). Вся загвоздка имхо в прошивке контроллера. *если требуется код проги выложу Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
NullPointer 0 6 апреля, 2009 Опубликовано 6 апреля, 2009 · Жалоба В ней не сомневаюсь. Частоту оценить не забудьте: длительность низкого\высокого уровня клоков должна быть не менее 2 тактов МК. Вся загвоздка имхо в прошивке контроллера. Может быть и в аппаратуре ;) Если AVCC не подключен к VCC, порт А работать не будет. Про SS я выше написал (отердактировал, м.б. не заметили). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
andrik.kiev.ua 0 6 апреля, 2009 Опубликовано 6 апреля, 2009 · Жалоба последовательно сигналам - резисторы Ом по 100, для согласования уровней и SS на GND (mega8 без SS не работает точно, не знаю как 16-я). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
=GM= 0 7 апреля, 2009 Опубликовано 7 апреля, 2009 · Жалоба Проблема в том, что оба этих варианта не работают, и цели поставленной задачи достичь не удалось Обратите внимание, как вы выталкиваете данные из лпт и как заталкиваете в ведомое устройство, возможно, проблема в этом. Сделайте симметричную команду. Если не сработает, то попробуйте просто принять любой байт и вывести его в портА на светодиоды, сразу будет видно, принимаете вы данные в принципе или нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GenaSPB 11 7 апреля, 2009 Опубликовано 7 апреля, 2009 · Жалоба Если AVCC не подключен к VCC, порт А работать не будет. Are you sure? В смысле - вы это точно знаете? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
NullPointer 0 7 апреля, 2009 Опубликовано 7 апреля, 2009 · Жалоба 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. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zi4rox 0 19 апреля, 2009 Опубликовано 19 апреля, 2009 · Жалоба попробуйте просто принять любой байт и вывести его в портА на светодиоды Так и делал 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; Подскажите, в чем здесь может быть заковырка? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SapegoAL 0 19 апреля, 2009 Опубликовано 19 апреля, 2009 · Жалоба Только что сделал бут ч/з SPI на базе ByteBlaster. Если надо, то могу процедуры нижнего уровня открыть. И про подводные камни рассказать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zi4rox 0 19 апреля, 2009 Опубликовано 19 апреля, 2009 · Жалоба Отписал Вам в личное сообщение, но не уверен, что оно дошло. Был бы очень благодарен, если бы поделились вашим опытом и информацией Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SapegoAL 0 19 апреля, 2009 Опубликовано 19 апреля, 2009 · Жалоба Дело в том, что 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". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sigmaN 0 19 апреля, 2009 Опубликовано 19 апреля, 2009 · Жалоба Я, конечно, мегу никогда не кодил, но как-то странно.... Может тип не тот...Или там оптимизатор выделывается Может добавить volatile к read.... Там какая-то очень простая ошибка должна быть по идее Это я в ответ на Так же не работает и попытка счиатать значение SPDR в переменную =( unsigned char read; read = SPDR; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zi4rox 0 19 апреля, 2009 Опубликовано 19 апреля, 2009 · Жалоба 2SasaVitebsk Спасибо, за столь развернутый пост. Буду разбираться теперь Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
andrik.kiev.ua 0 20 апреля, 2009 Опубликовано 20 апреля, 2009 · Жалоба инициализация 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; } повторюсь, без согласования входов по уровню (похоже КМОП защелкивались) - на двух компах из трех связь сбоила и не работала как надо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться