BASCOM hardware slave TWI (i2c)

Программирование микроконтроллеров AVR, PIC, ARM.
Разработка и изготовление печатных плат для модулей.

BASCOM hardware slave TWI (i2c)

Сообщение MiBBiM » 11 ноя 2009, 23:02

Либы для хардварного TWI в баскоме есть, но для TWIslave - ни софтварной, ни железной реализации не предусмотрено, поэтому решено было писать самим.
за основу взял код с немецкого форума, в нем реализован только режим ведомый-приемник на флагах ожидания.
Код: Выделить всё
$regfile = "M8def.dat"                                 ' the used chip
$crystal = 16000000                                    ' frequency used
'$baud = 9600    ' brauchen wir nicht

Waitms 100

' TWI init
Gosub Twi_init_slave

Config Servos = 2 , Servo1 = Portb.1 , Servo2 = Portb.2 , Reload = 8

' Ports für Servo auf Ausgang
Config Portb.1 = Output
Config Portb.2 = Output
Config Portd = Output

' Musik, wegen Stimmung usw. :-)
Sound Portb.0 , 300 , 450                              ' BEEP

Servo(1) = 100
Servo(2) = 100

Const Maxanzahlbyte = 10                               ' Wenn mehr Zeichen kommen werden diese verworfen !
Dim Messagebuf(maxanzahlbyte) As Byte
Dim Anzahlbuf As Byte                                 ' Anzahl Zeichen die gesendet wurden

Dim Neuemsg As Byte                                    ' zeigt an wenn eine neue Message gültig ist

Dim Twi_control As Byte                                ' Controlregister lokale kopie
Dim Twi_status As Byte
Dim Twi_data As Byte

Const Eigene_slave_adr = &H40                          ' Adresse evtl. Anpassen

' wegen der Servos, TWI braucht das hier nicht
Enable Interrupts

Twi_data = 0
Neuemsg = 0                                            ' Paket ungültig
Anzahlbuf = 0                                          ' Anzahl empfangener Bytes

Portd = &HFF                                           ' alle LEDs aus

'Print "M8 servo test"
Do

    ' schauen ob TWINT gesetzt ist
    Twi_control = Twcr And &H80                        ' Bit7 von Controlregister

    If Twi_control = &H80 Then
        Twi_status = Twsr And &HF8                     ' Status

        Portd = Not Twi_status                         ' test auf die LEDs

        Select Case Twi_status

            ' Slave Adress received, wir sind gemeint !
            Case &H60 :
                Twcr = &B11000100                      ' TWINT löschen, erzeugt ACK
                Anzahlbuf = 0
                Neuemsg = 0                            ' Flag für Message ungültig

            ' Byte mit ACK
            Case &H80 :
                If Anzahlbuf < Maxanzahlbyte Then
                    Incr Anzahlbuf                     ' zähler +1
                    Messagebuf(anzahlbuf) = Twdr
                End If
                Twcr = &B11000100                      ' TWINT löschen, erzeugt ACK

            ' Stop oder restart empfangen
            Case &HA0 :
                Twcr = &B11000100                      ' TWINT löschen, erzeugt ACK
                ' es müssen 3 Byte sein, damit das Paket OK ist
                If Anzahlbuf = 3 Then
                    Neuemsg = 1                        ' Flag für Message gültig
                Else
                    Neuemsg = 0                        ' Flag für Message ungültig
                End If

            ' letztes Byte mit NACK, brauchen wir nicht
            Case &H88 :
            Case &HF8 :
            ' Fehler, dann reset TWI
            Case &H00 :
                Twcr = &B11010100                      ' TWINT löschen, reset TWI

            ' was anderes empfangen, sollte nicht vorkommen
            Case Else :
                Twcr = &B11000100                      ' TWINT löschen, erzeugt ACK

        End Select

    End If

    ' ein gültiges Paket angekommen
    If Neuemsg = 1 Then
        Neuemsg = 0                                    ' Flag wieder löschen
        ' nur wenn das erste Zeichen ein "S" ist tun wir was damit !
        If Messagebuf(1) = "S" Then
            Servo(messagebuf(2)) = Messagebuf(3)
            Sound Portb.0 , 300 , 450                  ' Roger-BEEP
        End If
    End If

    Waitms 10

Loop

End

' TWI als slave aktivieren
Twi_init_slave:
    Twsr = 0                                                ' status und Prescaler auf 0
    Twdr = &HFF                                             ' default
    Twar = Eigene_slave_adr                                 ' Slaveadresse setzen
    Twcr = &B01000100                                       ' TWI aktivieren, ACK einschalten
Return

переписал на прерываниях и поперчил комментариями:
Код: Выделить всё
'(
  Программа для приема 3х байтных пакетов через TWI
')

$regfile = "M8def.dat"
$crystal = 7372800
$baud = 9600
Config Serialin = Buffered , Size = 20

Waitms 100

Const Max_buf_len = 10                                      ' максимальный размер буфера(пакета)
Dim Buf(max_buf_len) As Byte
Dim Buf_len As Byte                                         ' текущий размер буфера(пакета)

Dim New_msg As Byte                                         ' флаг нового пакета

Dim Twi_control As Byte
Dim Twi_status As Byte
Dim Twi_data As Byte

Enable Twi
On Twi Twi_int_slave

' TWI init
Gosub Twi_init_slave

Enable Interrupts

Print "M8 twi test"
Do
   ' если принят новый пакет
    If New_msg = 1 Then
        New_msg = 0                                         ' очистить флаг
        Print "message: " ; Buf(1) ; " " ; Buf(2) ; " " ; Buf(3)       ' вывести первые три байта
    End If
    Waitms 10
Loop

End

' TWI инициализация слейв - приемник, слейв - передатчик (зависит от старт-пакета)
Twi_init_slave:
   Const Twi_slave_adr = &B01110000                         ' адрес слейва (наш адрес), TWCG (нулевой бит) сброшен, не распознаем общий вызов(адрес 0x00)
   Twi_data = 0
   New_msg = 0                                              ' очищаем пакет
   Buf_len = 0
   Twsr = 0                                                 ' очищаем статус, частота обмена = fclk/(16 + 2*TWBR*4^TWPS), для ведомого состояние регистров безразлично
   Twdr = &HFF                                              ' обнуляем данные
   Twar = Twi_slave_adr                                     ' устанавливаем адрес слейва
   Reset Twar.twgce                                         ' запрет распознавания общего адреса
   Set Twcr.twen                                            ' разрешение работы модуля
   Set Twcr.twea                                            ' разрешение автоматической генерации подтверждения
   Set Twcr.twint                                           '!!!ВНИМАНИЕ!!!сбрасываем флаг прерывания
   Set Twcr.twie                                            ' разрешаем прерывания
Return

Twi_int_slave:
   Twi_status = Twsr And &HF8                               ' отбрасываем 3 лишних байта [-,TWSP1,TWSP2]
   Select Case Twi_status
      Case &H60 :                                           ' адресованы
         Buf_len = 0
         New_msg = 0
         Reset Twcr.twsto
         Set Twcr.twea
         'Twcr = &B11000100
      Case &H80:                                            ' приняли байт (их может быть много)
         If Buf_len < Max_buf_len Then
            Incr Buf_len
            Buf(buf_len) = Twdr
            Reset Twcr.twsto
            Set Twcr.twea
         End If
      Case &HA0 :                                           'повстарт или стоп в то время, когда устройство адресовано
         ' здесь надо бы начать передавать данные
         ' ежели приняли три байта, то выходим
         If Buf_len = 3 Then
            New_msg = 1                                     ' флаг нового сообщения установлен
         Else
            New_msg = 0                                     ' флаг нового сообщения сброшен
         End If
         Reset Twcr.twsta                                   ' освобождаем шину
         Reset Twcr.twsto
         Set Twcr.twea
         'Twcr = &B11000100

      Case &H88 :                                           ' устройство уже адресовано, был неверно принят байт данных и послан NACK
      Case &HF8 :                                           ' просто пакость в регистрах
      Case &H00 :                                           ' ошибка на шине
         Reset Twcr.twsta
         Set Twcr.twsto
         Set Twcr.twea
         'Twcr = &B11010100
      Case Else:
         Twcr = &B01000100                                  ' перегрузили шину
   End Select
Return

в ближайшей перспективе будет режим ведомый-передатчик.
Последний раз редактировалось MiBBiM 12 ноя 2009, 21:16, всего редактировалось 2 раз(а).
Tomorrow will be. Better
Аватара пользователя
MiBBiM
 
Сообщения: 1866
Зарегистрирован: 29 окт 2007, 18:11
Откуда: Пермь
прог. языки: Brainfuck/Basic/Delphi/C++/Lisp/x86asm/JavaScript

Re: BASCOM hardware slave TWI (i2c)

Сообщение MiBBiM » 12 ноя 2009, 21:10

полностью реализованный режим ведомого на прерываниях. написан для меги88, однако может быть использован на любом мк семейства mega с аппаратным TWI модулем.
Код: Выделить всё
'(
   Программа для приема пакетов по i2c, использет хардварный TWI.

   ВНИМАНИЕ, ОБЩИЕ ВЫЗОВЫ НЕ ПОДДЕРЖИВАЮТСЯ,
   ФЛАГ TWCG ДОЛЖЕН ВСЕГДА НАХОДИТСЯ В СБРОШЕННОМ СОСТОЯНИИ
')
$regfile = "m88def.dat"
$crystal = 7372800
$baud = 9600
Config Serialin = Buffered , Size = 20

' Настройка модуля
Const Twi_slave_adr = &B01110000                            ' !!содержимое TWAR!!, адрес слейва (7бит+[1 бит TWCG]), TWCG (нулевой бит) должен быть сброшен, т.е. не распознаем общий вызов(адрес 0x00)

Dim Twi_busy As Bit                                         ' флаг занятости модуля приемом/передачей

Const Max_msg_len = 10                                      ' максимальный размер буфера(пакета)
   Const Msg_expect_len = 3                                 ' ожидаемый размер принятого сообщения. если не равен текущему на момент окончания приема, то сообщение отбрасывается
   Dim Msg(max_msg_len) As Byte
   Dim Msg_len As Byte                                      ' текущий размер буфера(пакета)
   Dim Msg_new As Bit                                       ' флаг нового сообщения

Const Max_out_len = 10                                      ' максимальный размер буфера отправки
   Dim Msg_out(max_out_len) As Byte                         ' буфер отправки
   Dim Msg_out_len As Byte                                  ' текущий размер буфера отправки

Dim Twi_status As Byte                                      ' статус регистра TWSR
On Twi Twi_int_slave                                        ' конфигурация прерывания
Enable Twi


'!!!!
' пример заполнения сообщения для передачи
   Msg_out(1) = "B"                                         ' данные
   Msg_out(2) = "T"                                         'обратный порядок байт
   Msg_out(3) = "S"
   Msg_out_len = 3                                          ' размер
'!!!!

' Инициализация ведомого
Gosub Twi_init_slave

' Светодиод отладки
Config Pind.7 = Output : Led Alias Portd.7 : Led = 0

Enable Interrupts

Print "M88 twi test"
Do
   Led = Twi_busy                                           ' диод показывает состояние приема/передачи по TWI
   If Msg_new = 1 Then                                      ' если принят новый пакет
      Reset Msg_new                                         ' очистить флаг
      Print "message: " ; Chr(msg(1)) ; " " ; Chr(msg(2)) ; " " ; Chr(msg(3))       ' вывести первые три байта
   End If
   Waitms 10
Loop

End

' TWI инициализация слейв - приемник, слейв - передатчик (зависит от старт-пакета)
Twi_init_slave:
   Reset Msg_new                                            ' очищаем пакет
   Msg_len = 0
   Reset Twi_busy
   Twsr = 0                                                 ' очищаем статус, частота обмена = fclk/(16 + 2*TWBR*4^TWPS), для ведомого состояние регистров безразлично
   Twdr = &HFF                                              ' обнуляем данные
   Twar = Twi_slave_adr                                     ' устанавливаем адрес слейва
   Reset Twar.twgce                                         ' запрет распознавания общего адреса

   Set Twcr.twea                                            ' разрешение автоматической генерации подтверждения
   Reset Twcr.twsta
   Reset Twcr.twsto
   Set Twcr.twen                                            ' разрешение работы модуля
   Set Twcr.twie                                            ' разрешаем прерывания
Return

Twi_int_slave:
   Twi_status = Twsr And &HF8                               ' отбрасываем 3 лишних байта [-,TWSP1,TWSP2]
   Select Case Twi_status
   ' статусы приемника
   Case &H60:                                               ' адресованы SLA+W пакетом
      Msg_len = 0
      Reset Msg_new
      Set Twi_busy
      Reset Twcr.twsto                                      ' будет послан ACK
      Set Twcr.twea
   Case &H80:                                               ' адресованы, был принят байт данных и послан ACK
      If Msg_len < Max_msg_len Then                         ' приняли байт (их может быть много)
         Incr Msg_len
         Msg(msg_len) = Twdr
      End If
      If Msg_len <> Max_msg_len Then                        ' в буффер поместится следующий байт?
         Reset Twcr.twsto                                   ' ещё можем принимать данные, будет послан ACK
         Set Twcr.twea
      Else
         Reset Twcr.twsto                                   ' больше данных невозможно принять, будет послан NACK
         Reset Twcr.twea                                    '   прием адресного пакета заканчивается до тех пор, пока не будет сформирован стоп/повстарт на шине(побочный эффект)
      End If
   Case &H88:                                               ' адресованы, был принят байт данных, не помещающийся в буффер, т.е. послан NACK
      Reset Twcr.twsta                                      ' переключение в режим неадресованного ведомого
      Reset Twcr.twsto
      Set Twcr.twea
      Reset Twi_busy
   Case &HA0:                                               ' стоп или повстарт в то время, когда адресованы
      If Msg_len = Msg_expect_len Then                      ' принятые данные соотвествуют по размеру пакету?
         Set Msg_new                                        ' принятые данные - новое сообщение
      Else
         Reset Msg_new
      End If
      Reset Twcr.twsta                                      ' переход в режим неадресованного ведомого, распознавание SLA пакетов разрешено
      Reset Twcr.twsto
      Set Twcr.twea
      Reset Twi_busy
' статусы передатчика
   Case &HA8:                                               ' адресованы SLA+R пакетом
      Set Twi_busy
      Goto Twi_int_slave_state_b8
   Case &HB8:                                               ' был передан очередной байт данных и получен ACK (или только будет передан байт, если пришли из состояния &HA8)
Twi_int_slave_state_b8:
      If Msg_out_len = 0 Then                               ' данных для передачи нет?
         Twdr = &HFF                                        ' загрузили байт-заглушку, такое значение всегда принимает мастер, если приемник оключен
         Reset Twcr.twsto                                   ' это последний байт, NACK должен быть принят
         Reset Twcr.twea
      Else
         Twdr = Msg_out(msg_out_len)                           ' загрузили байт данных
         Decr Msg_out_len
         If Msg_out_len = 0 Then
            Reset Twcr.twsto                                   ' это последний байт, NACK должен быть принят
            Reset Twcr.twea
         Else
            Reset Twcr.twsto                                   ' будет послан очередной байт данных
            Set Twcr.twea
         End If
      End If
   Case &HC0:                                               ' был передан последний байт данных и получен NACK (конец передачи)
      Reset Twcr.twsta                                      ' переходим в режим неадресованного ведомого, разрешено распознавание вызовов
      Reset Twcr.twsto
      Set Twcr.twea
      Reset Twi_busy
   Case &HC8:                                               ' был передан последний байт данных и получен ACK (также означает, после последнего байта мастер хочет ещё один)
      Reset Twcr.twsta                                      ' переключение в режим неадресованного ведомого, разрешено распознавание вызовов
      Reset Twcr.twsto
      Set Twcr.twea
' общие состояния
   Case &HF8:                                               ' неопределенность состояния, в прерывании никогда не произойдет
   Case &H00:                                               ' ошибка на шине
      Reset Twcr.twsta
      Set Twcr.twsto
      Set Twcr.twea
      Reset Twi_busy
   Case Else:                                               ' неверный режим работы (например, если разрешено распознавание общих вызовов, которое не поддерживается!)
      'Twcr = &B01000100                                     ' перегрузили шину
   End Select
Return

код или принимает сообщение и выводит в уарт, или отсылает по запросу другое сообщение (но отсылает только один раз). чтобы пофиксить его под задачу, необходимо:
выставить адрес слейва
Код: Выделить всё
' Настройка модуля
Const Twi_slave_adr = &B01110000                            ' !!содержимое TWAR!!, адрес слейва (7бит+[1 бит TWCG]), TWCG (нулевой бит) должен быть сброшен, т.е. не распознаем общий вызов(адрес 0x00)

поменять размер буфер-пакета для приема, а также размер ожидаемого собщения
Код: Выделить всё
Const Max_msg_len = 10                                      ' максимальный размер буфера(пакета)
   Const Msg_expect_len = 3                                 ' ожидаемый размер принятого сообщения. если не равен текущему на момент окончания приема, то сообщение отбрасывается

поменять размер буфер-пакета для отправки (в связи с особенностями реализации, данные в него нужно заносить в обратном порядке):
Код: Выделить всё
Const Max_out_len = 10                                      ' максимальный размер буфера отправки

ну и заполнять/считывать сообщения в соотвествии с потребностями. не забывайте при этом изменять счетчики длины буферов
Код: Выделить всё
Dim Msg_len As Byte                                      ' текущий размер буфера(пакета)
Dim Msg_out_len As Byte                                  ' текущий размер буфера отправки

ниже проект симуляции. вводим терминале меги32 любой символ (это нужно лишь для задержки при передаче, нигде в дальнейшем произведенный ввод не используется). мега32-мастер отсылает меге88 3хбайтный пакет, который та незамедлительно перенаправляет в уарт. через небольшой промежуток времени мастер принимает пакет от слейва и также выводит в уарт. каждую последующую итерацию мега32 принимает только 0xFF (означающее, что слейву нечего передать), при этом все ещй отправляя сообщения. код мастера написан с помощью стандартных либ.
Вложения
twi_new.rar
(89.79 КиБ) Скачиваний: 23
Последний раз редактировалось MiBBiM 05 дек 2009, 19:53, всего редактировалось 1 раз.
Tomorrow will be. Better
Аватара пользователя
MiBBiM
 
Сообщения: 1866
Зарегистрирован: 29 окт 2007, 18:11
Откуда: Пермь
прог. языки: Brainfuck/Basic/Delphi/C++/Lisp/x86asm/JavaScript

Re: BASCOM hardware slave TWI (i2c)

Сообщение avr123.nm.ru » 12 ноя 2009, 22:08

Отличная работа !
Аватара пользователя
avr123.nm.ru
отсылающий читать курс
 
Сообщения: 14195
Зарегистрирован: 06 ноя 2005, 04:18
Откуда: Москва

Re: BASCOM hardware slave TWI (i2c)

Сообщение MiBBiM » 05 дек 2009, 19:57

переписал twi для меги88, исправил несколько неочевидных косячков, сильно очистил исходник, добавил новые плюшки, как то:
  • защита от переполнения буфера приема
  • защита от востребования данных при пустом буфере отправки (мастеру отправляются байты 0xFF)
  • добавил флаг занятости TWI_busy; может использоваться, например, для отключения других, длинных прерываний (которые могут нарушить целостность данных) во время акта приема/передачи.
  • ещё больше комментариев, почти в каждой строке
проблемы: не хочет заводится на частоте 100кГц (даже в симуляторе), пропускает все байты. на пониженной (я использовал 20кГц) все нормально.
Tomorrow will be. Better
Аватара пользователя
MiBBiM
 
Сообщения: 1866
Зарегистрирован: 29 окт 2007, 18:11
Откуда: Пермь
прог. языки: Brainfuck/Basic/Delphi/C++/Lisp/x86asm/JavaScript

Re: BASCOM hardware slave TWI (i2c)

Сообщение olegkaras » 10 июн 2020, 09:27

код или принимает сообщение и выводит в уарт, или отсылает по запросу другое сообщение (но отсылает только один раз).
Добрый день.
Как сделать чтобы сообщение отсылалось не один раз ?
olegkaras
 
Сообщения: 1
Зарегистрирован: 10 июн 2020, 09:20


Вернуться в Микроконтроллеры

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7