Свежие обсуждения
Микроконтроллеры

Софтовая часть программатора для ATMega16

1 2

Всем привет,

Скажу сразу - делаю курсовик, поэтому выбор лпт порта и "изобретание велосипеда" - самому не особо нравиться, но теперь уже разбираюсь и пытаюсь с этим бороться.

Задача: Программа, написанная на одном из языков высокого уровня для ПК, которая осуществляет запись скомпилированной программы с использованием одного из стандартных портов (LPT) в микроконтроллер (ATMEGA16). Записанная программа должна быть представлена в HEX коде.
* программатор стк200

Ситуация на данный момент следующая:

1. Задача по прежнему - написать софтину, которая прошивает амегу16
2. Научился управлять ЛПТ, т.е 1-0 подавать могу на какой либо из ног

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

Алогритм входа в режим программирования (из даташита):
1. Подать питание, при этом RESЕT и SCK должны быть 0.
2. Подождать как минимум 20 мс затем включить режим SPI Serial Programming
[послать 4 байта на MOSI]:
Byte1: 1010 1100
Byte2: 0101 0011
Byte3: xxxx xxxx // не важно что посылается
Byte4: xxxx xxxx // не важно что посылается
3. Проверить вошел ли контроллер в режим программирования.(Проверить синхронизацию). Когда режим синхронизации установлен, то 2ой байт ответит назад $53 запрашивая при этом 3ий байт. Не зависимо от того был ли правильно получен ответ или нет - нужно обязательно передать все 4 байта. Если этого не произошло, то задать 1 на RESET и повторить заново.

Вот, т.е хорошей проверкой того правильно я сделал или нет - будет то что при посылке 3его байта я получу в ответ $53. Но увы, пока этого не происходит, с чем и прошу помочь разобраться.

Прошу посмотрите как делаю в коде, возможно логика не та у меня и делаю не правильно:

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

Да и ещё, - по даташиту: Запись в контроллер происходит по фронту, а чтение из мк по спаду. Т.е если я правильно понял, чтобы мк считал 1 например - я сначала её выставляю, а потом подаю положительный импульс на SCK.

Поигрался с задержками импульсов, временем чтения выхода (MISO) - резульатат по прежнему один: в контроллер байты передаются от контроллера отклика не получаю. (Проверял это дело этой софтиной - xlpt - в мк байты идут, отклика нет)

Привожу ниже листинг того, что в коде творится теперь:

В глобальных переменных имеем 2 массива на входные и выходные 4 байта инструкций:


var
spi_cmd: array [1..4] of byte;
spi_out: array [1..4] of byte;

Процедура SPISend(spi_cmd); - которой в качестве параметра дается массив с 4мя байтами инструкции.
Посылает побитно на MOSI начиная с младшего бита. Также на каждый тактовый импульс считывает выход с мк (MISO) обрабатывает и в spi_out[] получаем 4 байта ответа (хотя поидее нужен только 1 - сделал на все)


procedure TForm1.SPISend (spi_cmd : array of byte);
var
cnt_byte, cnt_bit, pin: byte;
temp_in, temp_out: string;
begin
for cnt_byte:=1 to 4 do begin
temp_in:= IntToBin(spi_cmd[cnt_byte],8);
temp_out:= '';

for cnt_bit:=8 downto 1 do begin
if SCK_HIGH then SCK;

if MOSI_HIGH then
pin:= 1 else
pin:= 0;

if StrToInt(temp_in[cnt_bit]) <> pin then
MOSI;

SCK; // 1, фронт импульса

{Delay(10); // Длительность импульса}

If MISO_HIGH then
temp_out:= temp_out+'1' else
temp_out:= temp_out+'0';

SCK; // 0, спад импульса
end;

spi_out[cnt_byte]:= BinToInt(temp_out);

end;

end;

Небольшое пояснение:


MISO_HIGH : boolean; // Возвращает true если на входе 1
MOSI_HIGH : boolean; // Возвращает true если на входе 1
SCK_HIGH : boolean; // Возвращает true если на входе 1

MISO,MOSI,SCK,VCC,RESET // Инвертирует значение на ножке. Если было 1 сделает 0 и наоборот.

Теперь инструкция на Programming Enable [1010 1100 | 0101 0011 | xxxx xxxx | xxxx xxxx] выглядит так:


Lpt.WritePort(GetCurrentPort,LPT_DATA_REG,0); // SCK = 0 MOSI = 0 RESET = 0 VCC = 0
VCC; // VCC = 1
Delay(100);

spi_cmd[1]:= $ac;
spi_cmd[2]:= $53;
spi_cmd[3]:= 0;
spi_cmd[4]:= 0;

SPISend(spi_cmd);

if spi_out[3] <> $53 then
Application.MessageBox('Синхронизация не прошла. Эхо не получено!','ERROR',MB_OK);

Значение на выходе MISO не меняется вообще. Как стоит 1 так до конца и не меняется. В результате чего значение spi_out:
spi_out[1] = 255 ($ff)
spi_out[2] = 255 ($ff)
spi_out[3] = 255 ($ff)
spi_out[4] = 255 ($ff)

Пытался читать и вовремя прихода импульса и до и после - нужного результата не дало. Прошу помощи и свежего взгляда со стороны.

 

Первое, что бросается в глаза - у Вас задержка только между фронтом и спадом. Добавьте еще и между спадом и фронтом, т.е. в конце цикла перед чтением входа. А еще лучше так:

Выставили
Подождали
Фронт
Подождали
Спад
Подождали
Прочитали

Дайте время отработать драйверам и железу.

 

Кстати данные надо пихать и читать начиная со старшего бита (MSB). При этом первый бит СЧИТЫВАЕТСЯ еще до первого фронта SCK. (может я не ту диаграмму смотрю?)

 

Ну и тактовый сигнал Вы не забыли на XLAT1 подать?

 

Ув zi4rox, если интересно, могу выложить исходники своего программатора для Mega128 через COM, а также исходники еще одного своего, "автономного", программатора. Во вложении - его внешний вид.

 

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

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

 

В архиве - исходник для Mega128 и проект для WinAVR GCC. Меговский исходник постарее и с багом - не пишет несколько фузов подряд. Проект поновее и баг в нем устранен, но он не виндовый - его нужно для работы загружать в мегу8. Думаю, разберетесь Тактовый сигнал ни в том, ни в другом программаторе я на XLAT не подаю - программаторы ориентированы на внутрисхемное программирование, поэтому предполагается, что если тактовые импульсы нужны, то их источник в схеме присутствует.
Будут вопросы - мыло в профиле, правда, быстро ответить не обещаю

146861.zip

 

Получилось войти в режим программирования! Отклик получил.
Дело действительно было в том что надо было сначала MSB посылать. Не усмотрел это в ДШ.
Задержки вроде как не влияют вообще, импульсы доходят/читаються и без них, но для верности можно и оставить.

Через пару дней продолжу работу. Буду пробывать считать прошивку с мк. Если что не получиться - я к вам за помощью. Ещё раз всем спасибо за дельные идеи и советы.

 

Разорабрался, как и читать и ситрать и записывать прошивку в память. Конечно же первое время прыгал до потолка от радости - но тут же перестал, потому что работала прога совершенно неприемлимо долго! открыл для себя удивительный мир двоичной математики и переписал главную процедуру записи 4 байтов инструкции в память.

[code]


{это главная процедура, которая отвечает за передачу 4 байт инструкции на контроллер}

procedure TForm1.SPISend (data_buf: array of byte);
var
i,j: byte;
buffer: array [1..4] of byte;
const
SCK = $10; // 10000 - это 4 bit в порту LPT
MOSI= $20; // 100000 - это 5 bit в порту LPT
begin

{в массиве spi_cmd содержаться 4 байта инструкции - это глобальный массив - скопируем его в локальный buffer[]}

for j:=1 to 4 do
buffer[j]:=spi_cmd[j];

{теперь поехали - процедура передачи с 1 по 4 байт инструкции}

for j:=1 to 4 do
begin

{В байте у нас 8 бит, передача побитная - поэтому для каждого бита в байте имеем:}

for i:=1 to 8 do
begin
lpt_data_cache:= 0; // устанавливаем "ножки на ЛПТ" (но пока не пишем в порт) 0000 0000 - SCK, MOSI, RESET = 0

{Выставляем ножку MOSI в 1, если это так для данного бита в нашем байте из буффера}

if buffer[j] and $80 <> 0 then
lpt_data_cache:= lpt_data_cache or MOSI;
Lpt.WritePort(GetCurrentPort(),0,lpt_data_cache); // Пишем в порт то что в lpt_data_cache

lpt_data_cache:= lpt_data_cache or SCK; // устанавливаем SCK в 1
Lpt.WritePort(GetCurrentPort(),0,lpt_data_cache); // Пишем в порт то что в lpt_data_cache

buffer[j]:= buffer[j] shl 1; // Сдвиг в лево на 1 в текущем байте. (Переходим к следующему биту)

{Читаем что на ноге MISO - если 1 то, увеличиваем buffer[j] - увеличение идет с начала - так что постепеено в buffer[j] - появится ответный байт}

if MISO_HIGH then
inc(buffer[j]);
end;

{в spi_out - скопируем окончатенльыый байт что получили в ответ}

spi_out[j]:= buffer[j];

// У становим SCK в 0 to play safe

lpt_data_cache:= lpt_data_cache and not SCK;
Lpt.WritePort(GetCurrentPort(),0,lpt_data_cache);
end;

end;


[/code]

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

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

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

Сейчас процедура чтения выглядит так:

[code]


a_byte:= 0; // cтарший байт адреса
b_byte:= 0; // младший байт адреса
adress:= 0;

for end_adress:=0 to (DEVICE_SIZE div 2) do // DEVICE_SIZE=16896 делим на 2 потому что читаем младший и старший байты
begin

{формирую инструкцию на чтение младшего байта}

spi_cmd[1]:= $20; // младший байт
spi_cmd[2]:= a_byte;
spi_cmd[3]:= b_byte;
SPISend(spi_cmd); // отсылаем на мк

HexDump.Buffer[adress]:= spi_out[4]; // это буффер куда скалыдваются прочитанные байты
inc(adress); // идем дальше по адресу 0000, 0001, 0002, 0003

{формирую инструкцию на чтение старшего байта}

spi_cmd[1]:= $28; // старший байт
SPISend(spi_cmd);

HexDump.Buffer[adress]:=spi_out[4]; // По теущему адресу записываем прочитанный байт из spi_out[4]
inc(adress);

{Делаем перход из младшего в старший, например $1C0 превратиться в старший: $1 и младший $C0}

if b_byte >= $FF then
begin
b_byte:= 0;
inc(a_byte);
end;

inc(b_byte);
end;


[/code]

* 4 байта инструкции на чтение выглядят так:
Byte1: 0010 H000 // H = 0 - младший байт, H = 1 - старший байт
Byte2: 000a aaaa // - адрес
Byte3: bbbb bbbb // - адрес
Byte4: oooo oooo // - Отсюда читаем байт

Вот такие пироги - я и правда сейчас в ступоре и как улучшить произовдительность не знаю. Есть мысль, что не высокая скорость работы - вызвана нехорошей реализацией класса для работы с LPT.
(я его писал не сам, класс и драйвер был взят с сайта Варелия Кофтуна LPT)

Текущие характеристики:
Время чтения прошивки (16896 байт): 4 минуты
Скорость чтения: 70 байт/сек

* Насколько мне известно у лпт потолок около 2мб/сек - но таких больших то и не надо. Но 4 минуты - это невыносимо =((
в том же понипроге на моем же компьютере - чтение занимает раз в 7-8 меньше времени =(((

 

Боюсь, что здесь уже ничем не помочь - манипуляции ногами портов (что LPT, что COM) под Виндой свежее Win98 идут очень медленно. Я писал программаторы под COM-порты, там картина почти та же самая. Можно вытащить за цикл вызов функции GetCurrentPort (а лучше - вообще за функцию), но вряд ли это серьезно поможет.