Универсальный программатор

Универсальный программатор

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

Иногда в практике электронщика появляется проблема, связанная с программированием какой нибудь микросхемы флеш памяти. Как поступить? Покупать программатор – дорого, да, и пройдет время и он не поймет какую нибудь новую микросхему. Отдать кому нибудь – но кому  и опять деньги платить надо. Думаю, что в данной ситуации лучше всего собрать универсальный программатор самому. Ведь на самом деле задача программирования микросхемы памяти является достаточно примитивной. Необходимо на определенные выводы микросхемы подать определенные сигналы в определенной последовательности. То есть надо создать цифровой автомат. Если у вас есть минимальный опыт в работе с микроконтроллерами, то потратив не особенно много времени на реализацию это проекта, вы сделаете устройство, которое с легкостью сможете использовать для программирования чего угодно, внося при этом лишь небольщие изменения в конструкцию и программы. Необходимо лишь взглянуть на документацию по микросхеме, которую вы хотите запрограммировать.  А для управления программатором будем использовать компьютер. Что для этого надо – сущие пустяки.

  1. Колодка в которую можно вставить микросхему для программирования (самый важный компонент нашей системы).
  2. То, к чему подключим колодку – любой микроконтроллер, имеющий достаточное количество выводов для подключения, и с которым вы имеете достаточный опыт работы.
  3. Макетная плата, кому какая будет удобней.
  4. Несколько всяких деталек.
  5. Компьютер, имеющий последовательный порт, с подключенным интернетом  для  управления программатором и развлечения, пока идет процесс программирования.
  6. Установленная на компьютере система программирования.
  7. Конечно же, комплект документации на все эти штуки, который вы найдете в интернете.

В моем случае была необходимость запрограммировать микросхему SST49LF004B. Эта микросхема используется в качестве BIOS на многих системных платах. Для реализации проекта были выбраны:

Колодка от старой системной платы, выпаяная феном. К колодке аккуратно припаяна гребенка проводов, взятых из шлейфа жесткого диска. В качестве микроконтроллера использован 40- выводной корпус AVR микроконтроллера  ATMEGA16 c кварцевым резонатором на 11.0592 МГц. Макетная плата для монтажа без пайки. Несколько навесных компонентов, таких как резисторы, конденсаторы, регулятор напряжения.

При монтаже особое внимание надо уделить качеству разводки питания и установке блокирующих конденсаторов. Настоятельно рекомендую распаять один керамический конденсатор непосредственно на колодке для программирования микросхемы. Да, еще понадобится переходник для коммуникационного порта. Я использовал готовый, оставшийся от бог знает, какого разобранного устройства. Его можно собрать на паре транзисторов  или на микросхеме типа MAX232. Стабилизатор напряжения, собранный на регуляторе, имеет перемычку, определяющую выходное напряжение 3.3 В или 5.0 В. Микроконтроллер программируется на 5 В, затем работает и программирует  SST49LF004B на 3.3 В. Собираем все собранные штучки на монтажной плате и, соблюдая полярность, подключаем питание. Полную принципиальную схему не привожу, ибо макет, показанный на фотографии достаточно нагляден и собирается из того, что есть в наличии. Обращаю внимание лишь на то, что в соединении коммуникационных портов используются линии GND, RXD, TXD , а также RTS со стороны программатора (CTS со стороны компьютера). Ниже привожу фотографии готового программатора, не правда ли, простенько выглядит?

Собственно программатор …

                                             …и то, что к нему еще надо добавить.

Программное обеспечение создавалость по принципу Copy – Paste для решения конкретной задачи в кратчайшее время, поэтому отнестесь к качеству кода с пониманием. Программа для микроконтроллера писалась на Си в среде WinAVR под AVR Studio 4. При компиляции использовался ключ оптимизации –О1. Если вы используете другой кварц, необходимо изменить соответствующие значения, в зависимости от скорости обмена по последовательному интерфейсу.

#define F_CPU 11059200UL UBRRH = 0; // Baud rate 38400 (for CPU clock 11.0592 MHz) UBRRL = 17;

Запуск программатора происходит при посылке на последовательный порт (USART) соответствующего байта:

            1 – прочитать идентификатор микросхемы (для SST49LF004B два байта)

            2 – считать данные

            3 – стереть данные

            4 – записать данные

            5 –  прочитать 1к байт (для отладки)

Процедура wait1() используется для внесения небольшой дополнительной задержки, уж больно длинные проводочки использовались в схеме программатора. Звуковой сигнал используется как индикатор готовности,  да и просто прикольно. Все остальное закоментировано в тексте программы или достаточно очевидно. Далее, полный исходный текст программы, реализующий алгоритм программирования микросхемы SST49LF004B. Такой алгоритм имеют многие микросхемы, например W39V040A, 49LF040A, firmwsre hub SST49LF004A и др. Чаще всего они используются для реализации BIOS или Firmware различных устройств. Извиняюсь за коменты на английском языке, просто у меня тут все на английском.

/* ———————————————————————– Flash programming for SST49LF004B in parallel Programming mode Version 1.0 Date 12-nov-2011 MCU AVR ATMEGA16 MCU clock 11.0592 MHz, Communication – Baud rate 38400, 8-N-1 Pins Port A – address A7-A0 Port C – Address A10-A8 Port B – data Port D – control ———————————————————————– */ #define F_CPU 11059200UL #define BytesQty 524288 #include //#include //#include #include #include #include //control pins (PortD) #define SPEAKER 7 #define RTS_ 6 #define RST_ 5 #define RC_ 4 #define OE_ 3 #define WE_ 2 // TXD 1 // RXD 0 //old my frienfs from assembler – sbi and cbi #define sbi(var, mask) ((var) |= (uint8_t)(1 << mask)) #define cbi(var, mask) ((var) &= (uint8_t)~(1 << mask)) #ifndef NULL #define NULL ((void *)0) #endif uint32_t Haddr = 0x3FFFF; //0x3FFFF high address for SST49LF004B uint32_t _Address; uint32_t Address; uint32_t Address1; uint8_t data; uint8_t j=0; uint8_t i; uint8_t tmp; uint8_t tmp1; uint8_t buff[4]; //MCU init static void MCUInit(void) { ACSR = 0b10000000; // Analog comparator is OFF MCUCSR = 0b10000000; // JTAG desabled UCSRB = (1<<RXEN)|(1<<TXEN); // USART enabled UCSRC = 0b10000110; // Mode 8-N-1 UBRRH = 0;// Baud rate 38400 (for CPU clock 11.0592 MHz) UBRRL = 17; DDRB = 0b00000000; // Data PORTB = 0b11111111; // Pullup rezistors are on DDRA = 0b11111111; // Address A7-A0 DDRC = 0b11111111; // Address A10-A8 DDRD = 0b11111110; // Output PORTD = 0b11111111; // Control outputs _delay_ms(1); cbi(PORTD, RST_); //reset _delay_ms(1); sbi(PORTD, RST _delay_ms(10); cli();//all interrupts desabled } //------------------------------------------ //a little wait ;) static void wait1() { for(j=0;j<2;j++){} } //------------------------------------------ // Be..e..ee..p static void beep() { for( i=0;i<100;i++){ cbi(PORTD, SPEAKER); _delay_ms(1); sbi(PORTD, SPEAKER); _delay_ms(1); } } //------------------------------------------ static void USART_Receive(){ while (!(UCSRA & (1<<RXC))); // Wait for data to be received tmp = UDR; // Get and return received data from buffer } //------------------------------------------ static void USART_SendByte(uint8_t Data){ while((UCSRA&(1<<UDRE)) == 0); // Wait if a byte is being transmitted UDR = Data; // Transmit data } //------------------------------------------ static void WriteByte(uint32_t A, uint8_t d){ _Address = A; DDRB = 0b11111111; PORTA = (uint8_t) _Address; PORTC = (uint8_t)( _Address>>8); wait1(); cbi(PORTD, RC_); //Lock Row address wait1(); PORTA = (uint8_t)( _Address>>11); PORTC = (uint8_t)( _Address>>19); wait1(); cbi(PORTD, WE_); wait1(); PORTB = d; wait1(); sbi(PORTD, RC_); //Lock Column address wait1(); sbi(PORTD, WE_); DDRB = 0b00000000; wait1(); } //—————————————— uint8_t ReadByte(uint32_t A){ _Address = A; PORTB = 0b11111111; DDRB = 0b00000000; PORTA = (uint8_t) _Address; PORTC = (uint8_t)( _Address>>8); wait1(); cbi(PORTD, OE_); //Flash Output enable cbi(PORTD, RC_); wait1(); PORTA = (uint8_t)( _Address>>11); PORTC = (uint8_t)( _Address>>19); wait1(); sbi(PORTD, RC_); wait1(); sbi(PORTD, OE_); //Flash Output desable return PINB; } //—————————————— //Reading of data block static void ReadBlock (uint32_t bytes){ for (Address=0; Address<bytes; Address++) { buff[0] = ReadByte(Address); USART_SendByte(buff[0]); } } //—————————————— //Chip erase static void ChipErase () { WriteByte(0x5555,0xAA); WriteByte(0x2AAA,0x55); WriteByte(0x5555,0x80); WriteByte(0x5555,0xAA); WriteByte(0x2AAA,0x55); WriteByte(0x5555,0x10); for (i=0; i<3; i++) _delay_ms(100); //Chip-Erase Time < 100 ms } //------------------------------------------ // Reading of two ID bytes static void SoftwIde() { sbi(PORTD, OE_); DDRB = 0b11111111; wait1(); //Enter to software ID Entry WriteByte(0x5555,0xAA); WriteByte(0x2AAA,0x55); WriteByte(0x5555,0x90); _delay_ms(1); //Read ID bytes buff[0] = ReadByte(0x0000); buff[1] = ReadByte(0x0001); USART_SendByte(buff[0]); USART_SendByte(buff[1]); //Leave software ID Entry WriteByte(0x5555,0xAA); WriteByte(0x2AAA,0x55); WriteByte(0x5555,0xF0); } //------------------------------------------ // Byte-Program static void ProgrammByte(uint32_t A1, uint8_t d1){ WriteByte(0x5555,0xAA); WriteByte(0x2AAA,0x55); WriteByte(0x5555,0xA0); wait1(); WriteByte(A1,d1); //maximum Byte-Program time < 20 usec for SST49LF004B wait1(); //additional wait is time of waiting of the next byte } //------------------------------------------ int main(void){ MCUInit(); beep(); beep(); for (;;){ cbi(PORTD, RTS_); //RTS ON for COM Port USART_Receive(); if (tmp == 0x31) {//Chip Id reading, output - 2 bytes SoftwIde(); beep(); } if (tmp == 0x32) {//Reading data block (arg. is bytes q-ty) ReadBlock (BytesQty); beep(); } if (tmp == 0x33) { //Chip erase,It's clear ;) ChipErase (); beep(); } if (tmp == 0x34) { //Writing data block for (Address1 = 0; Address1 < BytesQty; Address1++){ cbi(PORTD, RTS_); //RTS ON for COM Port wait1(); USART_Receive(); tmp1 = tmp; sbi(PORTD, RTS_); //RTS OFF for COM Port Address = Address1; ProgrammByte(Address, tmp1); } beep(); } if (tmp == 0x35) { //Reading 1K data block ReadBlock (1024); beep(); } } }

Теперь перейдем к компьютеру. Для управления программатором используется компьютер с программой, написанной на С++ в среде Microsoft Visual C++ 2010 Express под операционкой Windows 7. На самом деле, подойдет любой компьютер с любой операционкой, а программу можно написать хоть на Паскале. Главное, чтобы у вас был опыт в этом. А если опыта нет, то для электронщика, не очень желаюшего стать программистом, рекомендую освоить Visual Вasic.  Освоив, вы поймете, какой мощный у вас появится помощник. 

Программа для компьютера написана достаточно небрежно. Просто была взята форма, туда были набросаны кнопки и текстовые окошки и по событию «щелчок на левой кнопке» писались соответствующие процедуры.

Так это выглядит программный интерфейс после прочтения ID и блока данных в 1 кБ:

 Здесь приведу лишь  некоторые важные процедуры из программы.

Инициализация коммуникационного порта (COM1):

HANDLE hCom; DCB dcb; DWORD d; DWORD EvtMask = 0; void CommPortInit() { if( hCom != NULL) CloseHandle( hCom ); hCom = CreateFile( L”COM1″, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING ,0, NULL); GetCommState (hCom, &dcb); dcb.BaudRate = CBR_38400; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; SetCommState (hCom, &dcb); SetCommMask (hCom, EV_RLSD ); COMMTIMEOUTS cto; cto.ReadIntervalTimeout = 0; cto.ReadTotalTimeoutMultiplier = 100; cto.ReadTotalTimeoutConstant = 0; cto.WriteTotalTimeoutMultiplier = 0; cto.WriteTotalTimeoutConstant =0; SetCommTimeouts (hCom, &cto);

Чтение блока данных (SIZE – количество байт для чтения):

#define SIZE 524288 //512 kB char buff[SIZE]; char buff1[SIZE]; HANDLE hFile; HANDLE hFile1; void ReadComPort(int lng){ for (i=0; i< SIZE; i++){ ReadFile( hCom, buff1, 1, &d, NULL ); buff[i] = buff1[0]; }

Чтение данных из микросхемы и запись в файл “flash_r.bin”, : private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { textBox1 -> Text = “Reading …”; PurgeComm (hCom, PURGE_RXCLEAR); TransmitCommChar(hCom, 2); //Transmit command “2” Sleep(10); ReadComPort(SIZE); //512kB hFile = CreateFile( L”flash_r.bin”, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); SetFilePointer( hFile, 0, NULL, FILE_END ); //Continue.. WriteFile( hFile, buff, SIZE, &d, NULL ); CloseHandle( hFile ); textBox1 -> Text = “File flash_r.bin is written”; }

Чтение  данных из микросхемы и  запись в файл "flash_r.bin",  :

private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { textBox1 -> Text = “Reading …”; PurgeComm (hCom, PURGE_RXCLEAR); TransmitCommChar(hCom, 2); //Transmit command “2” Sleep(10); ReadComPort(SIZE); //512kB hFile = CreateFile( L”flash_r.bin”, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); SetFilePointer( hFile, 0, NULL, FILE_END ); //Continue.. WriteFile( hFile, buff, SIZE, &d, NULL ); CloseHandle( hFile ); textBox1 -> Text = “File flash_r.bin is written”; }

Чтение данных из файла и запись в микросхему: Сравнение двух файлов:

private: System::Void button4_Click(System::Object^ sender, System::EventArgs^ e) { hFile = CreateFile( L”flash.bin”, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); //SetFilePointer( hFile, 0, NULL, FILE_BEGIN ); ReadFile( hFile, buff, SIZE, &d, NULL ); textBox1 -> Text = “Writing…”; textBox1 -> Refresh(); PurgeComm (hCom, PURGE_TXCLEAR); TransmitCommChar(hCom, Command[4]); Sleep(50); for (i=0;i<524288;i++){ TransmitCommChar(hCom, buff[i]); textBox1 -> Text = “Writing…”; textBox1 -> Refresh(); textBox1 -> Text = “Writing”; textBox1 -> Refresh(); textBox1 -> Text = “Writing…..”; textBox1 -> Refresh(); textBox1 -> Text = “Writing.”; textBox1 -> Refresh(); String^ clistr; sprintf_s( buff1, “%d “, i ); clistr = gcnew String(buff1); richTextBox1 -> Text = clistr; //Counter richTextBox1 -> Refresh(); } textBox1 -> Text = “Data to flash is writen”; }

Сравнение двух файлов:

private: System::Void button6_Click(System::Object^ sender, System::EventArgs^ e) { hFile = CreateFile( L”flash.bin”, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); ReadFile( hFile, buff, SIZE, &d, NULL ); hFile1 = CreateFile( L”flash_r.bin”, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); ReadFile( hFile1, buff1, SIZE, &d, NULL ); j=1; for (i=0;i< SIZE;i++){ if (buff1[i] != buff[i]) j=0; } if (j==0) textBox1 -> Text = “Error”; else textBox1 -> Text = “OK”; CloseHandle( hFile); CloseHandle( hFile1); }

Самое важное это правильно сконфигурировать коммуникационный порт. Обратите внимание на RTS_CONTROL_HANDSHAKE , а также на то, что для передачи и приема данных используются разные процедуры: TransmitCommChar(hCom, buff[i])  ReadFile( hFile, buff, SIZE, &d, NULL ). Компьютер передаст байт в порт только тогда, когда установится линия RTS со стороны программатора. При программировании не происходит ни каких проверок. Мы просто устанавливаем правильные временные задержки. Для проверки мы просто считываем данные из запрограммированной микросхемы и сверяем с оригиналом. Вот, пожалуй и все. Да здравствуют микроконтроллеры и визуальные средства программирования!

Visual Studio Express 2010 C++

AVR Studio 4.19

WinAVR

Скачать файлы проекта

Автор: Андрей Романов


Категория: Микроконтроллеры
Метки:

Написать коментарий

*
= 3 + 2

Добавить изображение

Последние статьи