Дело в том, что 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".