Поставлена задача, организовать параллельный интерфейс с внешним устройством в режиме EPP. Для CH341 вся информация на Си и это проблема которую пытаюсь решить. А проблема в следующем: При попытке получить статус выводов-приложение закрывается. Это небольшая часть кода для проверки связи с микроконтроллером Код (ASM): .elseif ax==IDM_WRITE mov CH341_MAX_NUMBER, 16 metka1: invoke CH341OpenDevice, CH341_MAX_NUMBER ;0 ; Открывает устройство с заданным номером. .if eax!=INVALID_HANDLE_VALUE mov iIndex, eax ; получили дескриптор устройства invoke CH341SetExclusive, CH341_MAX_NUMBER, 1; установили эксклюзивное использование invoke CH341SetParaMode,CH341_MAX_NUMBER, 1 ; режим ЕРР 1.9 возвращает (успех - 1) cmp eax, 0 ;jz metka3 jz metka2 invoke CH341ResetDevice, iIndex ; здесь всё хорошо PIC видит комп и наоборот ; Reset не нуждается в подтверждении mov al, 7 ; iIndex - не канает invoke CH341EppSetAddr, CH341_MAX_NUMBER, al ; передача, всё хорошо PIC видит, выводит 7 mov eax, 0 mov iStatus, eax stat: invoke CH341GetStatus, iIndex, iStatus ; с этого места программа закрывается cmp eax, 0 jz metka3 ;mov eax, iStatus ;bt eax, 10 ; проверка бита INT ;jc meka1 ; переход если CF=1 ;jnc stat ; переход если CF=0 ;cmp eax, 0 ;jz metka3 meka1: invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION ;"УСПЕШНОЕ ПОДКЛЮЧЕНИЕ!" invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION ;"ОТКЛЮЧИТЬ?" invoke CloseHandle, iIndex invoke CH341CloseDevice, CH341_MAX_NUMBER invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED invoke EnableMenuItem,hMenu,IDM_WRITE,MF_ENABLED invoke EnableMenuItem,hMenu,IDM_READ,MF_GRAYED ret jmp meka .else mov eax, CH341_MAX_NUMBER dec eax cmp eax, -1 jle metka2 mov CH341_MAX_NUMBER, eax jnl metka1 metka2: invoke MessageBox,NULL,addr Failure,NULL,MB_OK+MB_ICONERROR ;"ПОДКЛЮЧИТЕ УСТРОЙСТВО." invoke CloseHandle, iIndex invoke CH341CloseDevice, CH341_MAX_NUMBER ;invoke CloseHandle, iIndex ret jmp meka metka3: invoke MessageBox,NULL,addr Falure,NULL,MB_OK+MB_ICONERROR ; "УСТРОЙСТВО НЕ ОТВЕЧАЕТ!" invoke CloseHandle, iIndex invoke CH341CloseDevice, CH341_MAX_NUMBER ret jmp meka sosb1: invoke MessageBox,NULL,addr Filure,NULL,MB_OK+MB_ICONERROR ; "ОШИБКА ЧТЕНИЯ!" invoke CloseHandle,hMapFile mov hMapFile,0 invoke CloseHandle,hFileRead ret .endif Если я правильно понял, то нужно организовать процедуру обратного вызова, но как это сделать ума не приложу. Помогите разобраться и не судите строго ученика.
Это не решает проблему. Пробовал-осталось разобраться что и о чём в нём идёт речь. В общем не очень помогло.
f13nd, Уважаемый мне не отлаживать нужно. Программа ещё не готова, там ошибок море (я в этом не сомневаюсь). Меня интересует процедура обратного вызова которой у меня в тексте нет.
А, я понял: "проблема в следующем: приложение закрывается, но это не проблема и вообще надо какую-нибудь процедуру обратного вызова реализовать хз зачем". Судя по апи, процедур обратного вызова там всего две: Код (Text): typedef VOID ( CALLBACK * mPCH341_INT_ROUTINE ) ( ULONG iStatus ); BOOL WINAPI CH341SetIntRoutine( ULONG iIndex, mPCH341_INT_ROUTINE iIntRoutine ); typedef VOID ( CALLBACK * mPCH341_NOTIFY_ROUTINE ) ( ULONG iEventStatus ); BOOL WINAPI CH341SetDeviceNotify( ULONG iIndex, PCHAR iDeviceID, mPCH341_NOTIFY_ROUTINE iNotifyRoutine ); Создаешь процедуру (скорей всего stdcall), аргумент всего один (iStatus/iEventStatus), указатель на нее передаешь CH341SetIntRoutine/CH341SetDeviceNotify.
И как это всё записать на Ассемблере. (Я же не профи). Даже программу пришлось сочинять самому ибо желающих помочь даже за деньги не нашлось. Извини ,я только учусь программированию. Код (ASM): invoke CH341SetIntRoutine, iIndex, iIntRoutine ; invoke CH341SetDeviceNotify, iIndex, iDeviceID, iNotifyRoutine ; Я правильно себе представляю вызов функций API?
Наверное "addr iIntRoutine" надо в масмовском синтаксисе. Адрес процедуры передается как аргумент. --- Сообщение объединено, 22 авг 2023 --- ЗЫ: Код (Text): invoke CH341GetStatus, iIndex, iStatus Код (Text): BOOL WINAPI CH341GetStatus( ULONG iIndex, PULONG iStatus ); Приложение закрывается потому что ты вместо указателя на переменную, куда статус должен быть помещен, передаешь содержимое переменной. Отладчик мог бы об этом поведать, если бы это было проблемой.
То есть так ? Код (C): invoke CH341SetIntRoutine, iIndex, ADDR iIntRoutine ; Сборку провожу с помощью bat. файла. А проверку на реальном железе. А что значит - Определите процедуру обработки прерывания ? Код (C): BOOL WINAPI CH341SetIntRoutine( // Установите процедуру обработки прерывания ULONG iIndex, // Определите порядковый номер устройства CH341 mPCH341_INT_ROUTINE iIntRoutine ); // Определите процедуру обработки прерывания, если это будет НУЛЬ, сервис прерывания будет отменен, иначе стандартную программу назовут, когда прервано Вопросов океан.
Означает "впиши сюда адрес обработчика, либо подставь нуль чтобы отключить уже зарегистрированный обработчик". Обработчик прерывания в некотором смысле тоже процедура обратного вызова. Ну назвали так и назвали, не академики из академий видимо комментарий писали. ЗЫ: "иначе стандартную программу назовут, когда прервано" - да это ж перевод с китайского в стиле алиэкспресс.
Вот именно PROMT переводчик плюс ручная редакция , чтобы хоть что то понять. Уже опробовано. Работает! : Что же Вы, батенька, раньше-то молчали? invoke CH341GetStatus, iIndex, ADDR iStatus P.S. ЗЫ - знакомое, потому как в инете много инфы собираю и на форумах в том числе --- Сообщение объединено, 22 авг 2023 --- Спасибо огромное.
Проверил сегодня передачу данных и облом mov eax, 7 mov iBuffer, eax invoke CH341EppWriteData, iIndex, ADDR iBuffer, ioLength Вроде всё как положено, но на выходе ни WR ни DS не отработали вообще. Что опять не так?
Код (Text): BOOL WINAPI CH341EppWriteData( ULONG iIndex, PVOID iBuffer, PULONG ioLength ); Указатель на ioLength передается, не значение.
Вроде бы правильно BOOL WINAPI CH341EppWriteData( // Запишите данные в режиме EPP: WR# = 0 запись в, DS# = 0 передача, AS# = 1 приём, D0-D7=output ULONG iIndex, // Specify the serial number of the CH341 device PVOID iBuffer, // Указатель на буфер, куда данные, которые будут записаны, помещаются PULONG ioLength ); // Точки к единице длины, длина, которая будет выписана, когда введенный длина, которая будет выписана, и длина, на самом деле выписанная после возврата Пробовал и так invoke CH341EppWriteData, iIndex, ADDR iBuffer, ADDR ioLength результат тот-же --- Сообщение объединено, 23 авг 2023 --- Может со структурами перемудрил? Код (Text): USB_SETUP_PKT STRUCT ;; Передача управления USB фазы установления структуры пакета запроса данных mUspReqType BYTE ? ;; 00H Тип запроса mUspRequest BYTE ? ;; 01H Код запроса union ;; ;; struct mUspValueLow BYTE ? ;; 02H Младшие байты параметра, передаваемого по значению mUspValueHigh BYTE ? ;; 03H Параметр, передаваемый по значению высокие байты mUspValue WORD ? ;; 02H-03H Параметр, передаваемый по значению union1 ends union ;; ;; struct mUspIndexLow BYTE ? ;; 04H Индексные младшие байты параметра mUspIndexHigh BYTE ? ;; 05H Индексный параметр высокие байты mUspIndex WORD ? ;; 04H-05H Индексный параметр union2 ends mLength WORD ? ;; 06H-07H Длина данных - фазы данных USB_SETUP_PKT ENDS ;;;===================================================================================================== WIN32_COMMAND STRUCT ;; Определение структуры интерфейса команды WIN32 union ;; mFunction DWORD ? ;; Определите функциональный код или передайте число по каналу при вводе union1 mStatus DWORD ? ;; Операционное состояние возвратов на выводе mLength DWORD ? ;; Продолжительность доступа, возвращает длину последующих данных ends union ;; mUSB_SETUP_PKT DWORD ? ;; Управление USB передача данных запрашивает во время фазы установки mSetupPkt mBuffer DWORD ? ;; Буфер данных, от 0 до 255B в длине union2 ends WIN32_COMMAND ENDS ;;;========================================================================================================= Писал самостоятельно, а правильно или нет не у кого спросить. --- Сообщение объединено, 23 авг 2023 --- Мне кажется, что что то с буфером не так всё просто. Чего-то в нём не достаток. PVOID -это функция так-же? А какая? А как её расписать? А аргументы какие? А где она должна быть расположена в программе? А в буфер как её воткнуть? На Си пример есть, но он не годится для MASM32, а как переделать под ассемблер? Даже : stdcall mPCH341_NOTIFY_ROUTINE iStatus - синтаксис компилятору не нравится. --- Сообщение объединено, 23 авг 2023 --- К стати ОляДБГ выдаёт код ошибки 00000006 Не верный дескриптор если верить инету. Но как так?
В доках pdf всё расписание-же, вы читали их? Нотификация вам не нужна - если посмотреть на описание функции CH341SetDeviceNotify(), то её колбэк просто возвращает статус устройства в порту: подключено или удалено. Код (Text): CH341SetDeviceNotify (iIndex, iDeviceID, pCH341_NOTIFY_ROUTINE); pCH341_NOTIFY_ROUTINE (iEventStatus); const CH341_DEVICE_REMOVE = 0 ; CH341_DEVICE_REMOVE_PEND = 1 ; CH341_DEVICE_ARRIVAL = 3 ; Если в теории пролистав доки, то ваш алго должен быть примерно таким.. (все аргументы с префиксом(i) являются непосредственным значением, а префикс(р) - это указатель на память): 1. Подключаем девайс в порт USB и в диспетчере смотрим, в каком порту оно появилось. Номер порта и будет значением iIndex для всех функций в коде. Вроде была какая-то API, которая авто-возвращала этот номер.. не помню. В дебаг-версии пусть юзер сам предоставляет "iIndex" программе. Код (Text): // ********************* // General routines // ********************* // CH341A is addressed with ‘iIndex': // iIndex=0 => 1st CH341 @USB // iIndex=1 => 2nd CH341 @USB // iIndex=2 => 3rd ... 2. CH341OpenDevice (iIndex) для открытия девайса. Ошибка, если в EAX получим(-1). 3. CH341ResetDevice (iIndex), сбросить настройки чипа в дефолт. 4. CH341SetExclusive (iIndex, 1), чтобы никто больше не пользовался портом. 5. CH341InitParallel (iIndex, 1), перевести чип в режим EPP, iMode=1. 6. CH341SetParaMode (iIndex, 1), видимо свойства порта (константы iMode указаны на скрине ниже). 7. CH341GetDeviceDescr(iIndex, pBuffer, pLength), считаем в буфер дескриптор девайса (хз какого он размера). Буфер должен быть в памяти, а размер "pLength" там-же в переменной. Код (ASM): .data pLength dd 128 pBuffer db 128 dup(0) 8. CH341GetDeviceName(iIndex), в регистре EAX получим указатель на имя где-то в памяти. 9. CH341EppReadData(iIndex, pBuffer, pLength), читаем с порта в буфер (Write пишет в порт из буфера). 10. CH341EppSetAddr (iIndex, iAddr), если читаем из адреса, то указываем его. 11. CH341EppReadAddr(iIndex, pBuffer, pLength) 12. CH341CloseDevice(iIndex). Это все функции для работы с парпортом в режиме EPP. В доках указано, что CH341GetStatus() используется только при работе с шиной i2c.
Вот небольшой пример для работы с девайсом CH341. Поскольку у меня нет его физически, то код писал в слепую, просто вызывая функции. 1. Как и говорилось выше, значение "iIndex" смотри в диспетчере устройств - на входе, код запросит его. Кстати, чтобы программно поймать "iIndex", можно обратиться к диспетчеру конфигурации Win, функции которого сосредоточены в либе "setupapi.dll" - они имеют префикс CM_xx от "ConfigManager". Но это длинная история, и можно пойти другим путём. В хидере ch341.h есть константа "mCH341_MAX_NUMBER=16" - это макс.значение "iIndex". Достаточно просто организовать цикл на входе в программу, и перебирать "iIndex" в диапазоне 0-15. Как-только fn. CH341ResetDevice() вернёт нуль, значит ей попался валидный "iIndex". 2. CH341GetVersion() возвращает версию юзер-либы "ch341dll.dll", а CH341GetDrvVersion() должна возвратить версию драйвера "ch341wdm.sys". Походу дров загружается только при обнаружении физ.девайса, т.к. я получил dll v3.3, а в sys пусто (должно быть v3.4). 3. Код чекает ошибки, по которым можно сделать выводы. В скрепке EXE и инклуд для FASM'a "ch341header.inc". Код (ASM): format pe console include 'win32ax.inc' include 'equates\ch341header.inc' entry start ;//---------- .data iIndex dd 0 errMes rb 256 pLength dd mDEFAULT_BUFFER_LEN ;// размер буфера 1024 byte pBuffer rb mDEFAULT_BUFFER_LEN ;// сам буфер ;//---------- .code start: ;// Запрашиваем номер порта USB, куда подключен 'CH341' cinvoke printf,<10,' CH341 chip EPP-mode',\ 10,' max iIndex value = %d dec',\ 10,' -------------------------------',10,\ 10,' Type USB/COM iIndex: ',0>,mCH341_MAX_NUMBER cinvoke scanf,<'%d',0>,iIndex dec [iIndex] ;// Открываем девайс, сбрасываем его в дефолт, и ставим режимы invoke CH341OpenDevice,[iIndex] cinvoke printf,< ' Open device: ',0> call LastError invoke CH341ResetDevice, [iIndex] invoke CH341SetExclusive,[iIndex],1 invoke CH341InitParallel,[iIndex],mCH341_PARA_MODE_EPP19 invoke CH341SetParaMode, [iIndex],mCH341_PARA_MODE_EPP19 cinvoke printf,< ' Set EPP-mode v1.9: ',0> call LastError ;// Выводим различную инфу о девайсе invoke CH341GetDeviceDescr,[iIndex],pBuffer,pLength cinvoke printf,<10,' Device descriptor..: %08x%08x',0>,dword[pBuffer],dword[pBuffer+4] mov [pLength],256 invoke CH341GetConfigDescr,[iIndex],pBuffer,pLength cinvoke printf,<10,' Config descriptor..: %08x%08x',0>,dword[pBuffer],dword[pBuffer+4] invoke CH341GetDeviceName, [iIndex] push eax invoke CH341GetVersion push eax invoke CH341GetDrvVersion pop ebx ecx cinvoke printf,<10,' Device name........: %s',\ 10,' Library version....: %02d',\ 10,' Driver version....: %02d',10,0>,\ ecx,ebx,eax ;// Очистим буфер, и пробуем что-нибудь считать в него из порта stdcall ClearBuffer invoke CH341EppReadData,[iIndex],pBuffer,[pLength] cinvoke printf,<10,' Port read data.....: %08x%08x',10,0>,dword[pBuffer],dword[pBuffer+4] cinvoke printf,< ' Read lastError: ',0> stdcall LastError ;// Очистим буфер, введём в него строку, и пробуем записать её в порт stdcall ClearBuffer cinvoke printf,<10,' String for write...: ',0> cinvoke scanf,<'%s',0>,pBuffer mov [pLength],64 invoke CH341EppWriteData,[iIndex],pBuffer,[pLength] cinvoke printf,< ' Write lastError: ',0> stdcall LastError ;// Прихлопнуть девайс invoke CH341CloseDevice,[iIndex] cinvoke printf,< ' Close lastError: ',0> stdcall LastError @exit: cinvoke _getch cinvoke exit,0 ;//---------- proc ClearBuffer mov edi,pBuffer xor eax,eax mov ecx,128/4 rep stosd ret endp proc LastError invoke GetLastError invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,0,eax,0,errMes,256,0 invoke CharToOem,errMes,errMes cinvoke printf,<'%s',0>,errMes ret endp ;//---------- section '.idata' import data readable library msvcrt,'msvcrt.dll', kernel32,'kernel32.dll',\ user32,'user32.dll', ch341dll,'ch341dll.dll' include 'api\kernel32.inc' include 'api\user32.inc' include 'api\msvcrt.inc' include 'api\ch341dll.inc'
Marylin, благодарю за исчерпывающий ответ. Но меня смущает следующее : HANDLE WINAPI CH341OpenDevice( ULONG iIndex ) Согласно правила определения функции API запись слева направо и в начале ставится возвращаемое значение затем название и затем следуют аргументы. Если я не ошибаюсь ,то возвращаемое значение есть HANDLE , то есть некий дескриптор или описатель (тут кто как говорит) может его лучше называть INTEGER-некоторое целое число, а то возникает вопрос что с ним дальше делать и нужно ли его сохранять или используется только для идентификации? MASM и FASM несколько отличаются и, для ученика вроде меня, это существенно: Код (ASM): .elseif ax==IDM_WRITE metka1: invoke CH341OpenDevice, CH341_MAX_NUMBER ;0 ; Открывает устройство с заданным номером. .if eax!=INVALID_HANDLE_VALUE invoke CH341SetExclusive, CH341_MAX_NUMBER, 1; установили эксклюзивное использование invoke CH341SetParaMode,CH341_MAX_NUMBER, 1 ; режим ЕРР 1.9 возвращает (успех - 1) cmp eax, 0 jz metka2 invoke CH341ResetDevice, iIndex ; здесь всё хорошо PIC видит комп и наоборот ; Reset не нуждается в подтверждении mov al, 7 invoke CH341EppSetAddr, CH341_MAX_NUMBER, al ; передача, всё хорошо видит stat: invoke CH341GetStatus, iIndex, ADDR iStatus ; ; iStatus ; данные состояния прерывания чтения, посмотрите ниже ; Разряды соответствуют контактам D7-D0 CH341 ; Бит 8 соответствует ERR# , бит 9 - PEMP, бит 10 - INT# и бит 11 - SLCT mov eax, iStatus bt eax, 10 ; проверка бита INT jc stat ; переход если CF=1 mov al, 7 mov iBuffer, al ; запись в буфер числа =7 mov al, 1 mov ioLength, al ; количество байт=1 invoke CH341EppWriteData, CH341_MAX_NUMBER, ADDR iBuffer, ioLength meka1: invoke MessageBox,NULL,addr Success,addr AppName,MB_OK+MB_ICONINFORMATION ;"УСПЕШНОЕ ПОДКЛЮЧЕНИЕ!" invoke MessageBox,NULL,addr Unload,addr AppName,MB_OK+MB_ICONINFORMATION ;"ОТКЛЮЧИТЬ?" invoke CH341CloseDevice, CH341_MAX_NUMBER invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED invoke EnableMenuItem,hMenu,IDM_WRITE,MF_ENABLED invoke EnableMenuItem,hMenu,IDM_READ,MF_GRAYED ret jmp meka .else mov eax, CH341_MAX_NUMBER dec eax cmp eax, -1 jle metka2 mov CH341_MAX_NUMBER, eax jnl metka1 В документе ch341a_datasheet_code CH341GetStatus и CH341GetInput относятся к информационным запросам независимо от режима и нет ограничений на комбинирование команд, исключением являются команды прямого ввода/вывода- с осторожностью. Конечно код программы зависит от программы самого устройства и в данном случае подтверждение приходит от устройства на вывод INT. --- Сообщение объединено, 26 авг 2023 --- А ещё вопрос О существовании какой-нибудь программы которая сама пишет инклуды и прототипы (МЕЧТА).