Программатор для EEPROM с интерфейсом I2C

Притащили мне намедни в ремонт телик. Довольно-таки старенький, еще на базе вакуумно-люминесцентной трубе. Анамнез неисправности — нет свечения экрана, хотя при включении слышны характерные щелчки при стекании высокого напряжения. Характерный звук кадровой развертки тоже отсутствует, но, тем не менее, осциллограф показывает, что с кадровой всё нормально. Напряжения на трубе (на катодах, на ускоряющих электродах) в норме — так и должно быть — труба «закрыта». Накал имеется, тоже без вопросов.

Хождение по интернетам показало, что возможны две причины либо слетела прошивка у микросхемы памяти 24C02, либо издох видеопроцессор.

Я выпаял 24C02, ибо это несложно, но вот, программатора для этих дел у меня нет. Пришлось сочинять из того, что есть.

IMG_1225

IMG_1226

В программаторе использованы CH340G, XC6206R332MR, два транзистора 2N7002 и несколько резисторов и конденсаторов.

Плату развел с «косяком» — а куда мы без косяков-то! Пришлось оперативно резать дорожки бросать перемычки. Переживать не надо, всё удачно закончилось!

Микросхема CH430G — это конвертер USB-UART. Но здесь она используется не по основному своему назначению. Здесь она работает в режиме bit-banding — то есть компьютер программно управляет маханием её ножек. Комповая программа управляет сигналами RTS и DTR и считывает состояние CTS и DSR.

Поскольку интерфейс подразумевает открытый сток (открытый коллектор), то напрямую выводы (микросхемы) RTS и DTR использовать нельзя. Пришлось их «спрятать» за транзисторами.

Собственно, это — классическое решение, никакого know-how здесь нет. Все делается программно. Программа написана на Питоне. Но это тоже не принципиально.

Единственное, о чём я хотел бы предупредить, что подобное построение программаторов чревато огромными (просто фантастическими!) временными интервалами.

Почти после каждой смены состояния SDA и SCL нужно выполнять таймаут. Поскольку пакеты по USB бегают каждую миллисекунду, и нежелательно чтобы в одном пакете присутствовали смены обоих сигналов. Другими словами, после каждой смены состояния SDA и SCL нужно вводить таймаут на 1 мс. Да и сам Питон не позволяет устанавливать менее короткие интервалы времени, Да и сам он — он ведь не сам по себе работает, а подчиняется общему для всех программ регламенту в операционной системе.

В общем, чтение 256 байт из 24C02 выливается в 11 секунд. Длительность записи я не измерял, но что-то в пределах 12-15 секунд. Это без верификации.

Программа на Питоне не сложная. Состоит она из трех модулей:

Файл prog24.py

#!/usr/bin/env python
#coding:utf-8

''' prog24.py '''


import sys
import c02

help = '''  Используйте: prog24 порт тип команда файл"
    порт : порт подключения программатора (например, /dev/ttyUSB0)
    тип: тип микросхемы (например, 24c02)"
    команда:
      r -- чтение EEPROM в файл"
      w -- запись EEPROM из файла"
      c -- стирание EEPROM"
'''

def cleaneeprom():
  print "Очищаем"
  ee = c02.C02(sys.argv[1], pages, bytes, writetime)
  ee.cleanup()


def readeeprom():
  print "Считываем в файл", sys.argv[4]
  ee = c02.C02(sys.argv[1], pages, bytes, writetime)
  mem = ee.read()
  with file(sys.argv[4], 'wb') as fout:
    fout.write(mem)


def writeeeprom():
  print "Запысываем из файла", sys.argv[4]
  ee = c02.C02(sys.argv[1], pages, bytes, writetime)
  mem = bytearray(pages * bytes)
  with file(sys.argv[4], 'rb') as fout:
    mem = bytearray(fout.read())
  ee.write(mem)


def eetype():
  if sys.argv[2].upper() == '24C02':
    global pages
    global bytes
    global writetime
    pages = 32
    bytes = 8
    writetime = 0.01
    return True
  else:
    return False


if __name__ == "__main__":
  if len(sys.argv) == 4:
    if eetype():
      if sys.argv[3] == 'c':
        cleaneeprom()
        exit()
      else:
        print help
        exit()
    else:
      print "Ошибка: Неустановленный тип микросхемы"
      exit()
  elif len(sys.argv) == 5:
    if eetype():
      if sys.argv[3] == 'r':
        readeeprom()
        exit()
      elif sys.argv[3] == 'w':
        writeeeprom();
        exit()
      else:
        print help
        exit()
    else:
      print "Ошибка: Неустановленный тип микросхемы"
      exit()
  else:
    print help
    exit()

Файл c02.py

#!/usr/bin/env python
#coding:utf-8

''' c02.py '''

import time
import i2c

class C02(i2c.I2C):

  def __init__(self, port, pages, bytes, writetime):
    i2c.I2C.__init__(self, port)
    self.stop()
    time.sleep(0.05)
    self.pages = pages  # Количество страниц памяти в микросхеме
    self.bytes = bytes  # Количество байт на странице
    self.memsize = pages * bytes  # Общий размер памяти
    self.writetime = writetime
  
  def read(self):
    buf = bytearray(self.memsize)
    
    self.start()
    
    ack = self.sendbyte(0xA0)  # Адрес устройства    
    if ack == True:
      print "Микросхема не отвечает"
      exit()

    ack = self.sendbyte(0)  # Адрес ячеки    
    if ack == True:
      print "Микросхема не отвечает"
      exit()

    self.start()
              
    ack = self.sendbyte(0xA0 | 0x01)  # Ещё раз адрес устройства с установленным битом чтения
    if ack == True:
      print "Микросхема не отвечает"
      exit()
    
    self.sda1()
    for i in range(self.memsize):
      if i == self.memsize - 1:
        byte = self.readnextbyte(True)   # Читаем последний байт
      else:
        byte = self.readnextbyte(False)  # Читаем очередной байт
      
      buf[i] = byte
    
    self.stop()
    
    return buf
  

  def cleanup(self):
    for p in range(self.pages):
      self.start()
    
      ack = self.sendbyte(0xA0)  # Адрес устройства    
      if ack == True:
        print "Микросхема не отвечает"
        exit()

      ack = self.sendbyte(p * self.bytes)  # Адрес начала страницы
      if ack == True:
        print "Микросхема не отвечает"
        exit()

      for b in range(self.bytes):
        ack = self.sendbyte(0xFF)
        if ack == True:
          print "Микросхема не отвечает"
          exit()
          
      self.stop()                 # Запись страницы началась
      time.sleep(self.writetime)  # Время для окончания процесса записи


  def write(self, buf):
    for p in range(self.pages):
      self.start()
    
      ack = self.sendbyte(0xA0)  # Адрес устройства    
      if ack == True:
        print "Микросхема не отвечает"
        exit()

      ack = self.sendbyte(p * self.bytes)  # Адрес начала страницы
      if ack == True:
        print "Микросхема не отвечает"
        exit()

      for b in range(self.bytes):
        ack = self.sendbyte(buf[p * self.bytes + b])
        if ack == True:
          print "Микросхема не отвечает"
          exit()
          
      self.stop()                 # Запись страницы началась
      time.sleep(self.writetime)  # Время для окончания процесса записи

  

Файл i2c.py

#!/usr/bin/env python
#coding:utf-8

''' i2c.py '''


from serial import *
import time

class I2C:

  def __init__(self, port):
    try:
      self.ser = Serial(port)
    except SerialException:
      print "Подключите программатор!"
      exit(1)

  # Управление линиями SDA и SCL
  def sda1(self):
    self.ser.setRTS(True)
    time.sleep(0.001)

  def sda0(self):
    self.ser.setRTS(False)
    time.sleep(0.001)

  def scl1(self):
    self.ser.setDTR(True)
    time.sleep(0.001)

  def scl0(self):
    self.ser.setDTR(False)
    time.sleep(0.001)
    

  # Получение состояния линий SDA и SCL
  def sda(self):
    return not self.ser.getCTS()

  def scl(self):
    return not self.ser.getDSR()
    

  # Формирование условий "Старт" и "Стоп"
  def start(self):
    self.scl1()
    self.sda1()
    self.sda0()
    self.scl0()

  def stop(self):
    self.sda0()
    self.scl1()
    self.sda1()
  
  # Передать байт вернуть флаг подтверждения
  # Предролагается, что перед входом в метод SDA = 0 и SCL = 0
  def sendbyte(self, byte):
    for i in range(8):
      bit = (byte >> (7 - i) & 0x01)
      if bit == 0x01:
        self.sda1()
      else:
        self.sda0()
        
      self.scl1()
      self.scl0()

    self.sda1()
    # Девятый импульс
    self.scl1()
    ack = self.sda()
    self.scl0()
    
    return ack
    
  # Читаем из EEPROM следующий байт
  def readnextbyte(self, ack):
    byte = 0x00
    
    for i in range(8):
      self.scl1()
      bit = self.sda()
      self.scl0()
  
      if bit:
        byte |= (1 << (7 - i))
        
    # Формируем подтверждение
    if ack:
      self.sda1()
    else:
      self.sda0()
  
    self.scl1()
    self.scl0()
  
    self.sda1()
    
    return byte

Текст проги привожу как есть, я потерял интерес к проекту. Если кому хочется адаптировать его для других микросхем, то вместо модуля c02 (для 24С02) вам нужно написать свой (или свои) по аналогии с ним.

В общем-то всё работает, но я считаю, что прога годится только для относительно маленьких микросхем типа 24C02 или уж совсем на краний случай. Если прогу применять для 24C64 (8 кБайт), то время считывания будет уже составлять около 5 минут. А для 24C256 — вообще чуть ли не полчаса!

Себестоимость программатора получается вообще копеечная (CH340G 20 рублей, XC6206 — 6 рублей, кварцевый резонатор рублей 15, остальные детальки вообще сняты со старх плат). Идея заложенная в констркуцию программатора тоже достаточно интересная — программатор крайне простой и самодостаточный. Программатору не нужен внешний истоник питания, а разъемы USB есть в каждом компе. Ненавязчиво решено переключение питания и уровня сигналов для EEPROM (3.3 В и 5.0 В).

Но, тем не менее, я считаю, что это направление программаторов — тупиковое.

С моей точки зрения программаторы для 24Cxx следует делать на базе микроконтроллеров. Ну, то есть таймингами (bit binding-ом) должен заниматься не компьютер, а микроконтроллер. А связь микроконтроллера и компа должна осуществяляться на уровне передачи блоков инфоромации. При этом скорость считывания 24С256 теоретически будет около секунды, а записть — около трёх. Но в этом случае люди, которые не имеют опыта работы с микроконтроллерами, окажутся в затруднитьном состоянии. Тогда уж проще купить китайский за 100 рублей (или сколько он там может стоить) и подождать пару тройку недель.

One response to “Программатор для EEPROM с интерфейсом I2C

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s