Вы наверно будете смеяцца (i2c lcd и 1.8.7 IDE)

inspiritus
Онлайн
Зарегистрирован: 17.12.2012

если раньше проблема заключалась в выведении одного символа и решалась добавлением return 1; в двух местах, то теперь екран висит вообще и пять симпробованных библиотек с имеющимися и добавленными ретурнами ничем не помогли.

может быть кто то уже победил это постоянное гавнищще.

вроде под 1,8,5 работало норм с ретурнами, но оно стоит на компе в 500 км и буду там через 3 недели... а на ихем сцайте старые версии не нашел.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Тут они  все   прячутся:

https://www.arduino.cc/en/Main/OldSoftwareReleases#previous

inspiritus
Онлайн
Зарегистрирован: 17.12.2012

Спасибо, может все-таки есть у кого некривая библа для i2c lcd?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В том самом arhat.h на гитхабе есть полностью автономный код для I2C, работающий и не зависимый от остальной части. Можно выдрать и пользовать (что тут уже делали не раз). Настройки скорости вплоть до 880кГц, есть примеры применения. Буферизация - внешняя, что позволяет настраивать и экономить размер памяти. Режимы компиляции позволяют сократить код до требуемой части (без многомастерных режимов I2C), есть возможность компилировать с "логированием" передач (сколько принято, передано, кол-во ошибок и т.д.).

P.S. Сорри, возможность логирования передач тут отсутствует, есть в другой версии. Думаю не требуется.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015
/**
 * Работа с TWI (I2C, 2-Wire) интерфейсом через аппаратные прерывания.
 *
 * ! Аппаратно не бывает одновременно режима Master-RX и Slave-RX - поэтому тут используется ОДИН буфер.
 * ! Нет никаких "проверок", настройка глобалов - обязательна до каждого запуска автомата:
 * Master-Transmitter: twiMT_Ptr, twiMT_Count                  -- откуда и сколько байт свистеть;
 * Master-Receiver:    twiRX_Ptr, twiRX_Count, twiMasterReader -- куда и сколько принять и (опц.)что потом сделать;
 * Slave-Receiver:     twiRX_Ptr, twiRX_Count, twiSlaveReader  -- куда и сколько принять и (обяз.)что сделать;
 * Slave-Transmitter:  twiST_Ptr, twiST_Count, twiSlaveWriter  -- откуда и сколько отдать и (обяз.)что потом сделать;
 *
 * Особенности и отличия от типовых разработок (собрано многое вместе):
 * 1. Возможность одновременной работы как в режиме Master так и в режиме Slave;
 * 2. Внешняя буферизация данных при необходимости. Размер памяти определяется вашей программой, а не тут;
 * 3. Возможность ведения статистики - управляется константами компиляции;
 * 4. Произвольная настройка скорости работы интерфейса от 490гц до 1Мгц с авто вычислением прескалера и регистра скорости
 * 5. Возможность уменьшенной комплиции, если не требуются все режимы сразу.
 *
 * Константы компиляции #define (вставить в скетч перед инклудом этого файла нужное):
 * #define TWI_ON 1  // Master-Transmitter only: компилировать как режим Мастера-передатчика    ,ISR(TWI)=190 байт
 * #define TWI_ON 2  // Master-Receiver only:    компилировать как режим Мастер-приемник        ,ISR(TWI)=224 байт
 * #define TWI_ON 4  // Slave-Receiver only:     компилировать как режим Ожидающий получатель   ,ISR(TWI)=202 байт
 * #define TWI_ON 8  // Slave-Transmitter only:  компилировать как режим Отправитель по запросу ,ISR(TWI)=188 байт
 *
 * !!! Допускается компиляция нескольких режимов одновременно:
 * #define TWI_ON (1+2+4+8) // Включить в компиляцию ВСЕ режимы. ,ISR(TWI)=424 байт.
 *
 * @author Arhat109-20160402. arhat109@mail.ru
 * @license:
 *   1. This is a free software for any using and distributing without any warranties.
 *   2. You should keep author tag with any changes. May be with adding.
 *   Это свободное ПО для любого использования без каких-либо гарантий и претензий.
 *   Требуется сохранять тег @author при любых изменениях. Можете дописать свой.
 */
#ifndef _ARHAT_TWI_H_
#define _ARHAT_TWI_H_ 1

#define TWI_MASTER_TX    1
#define TWI_MASTER_RX    2
#define TWI_IS_SLAVE_RX  4
#define TWI_IS_SLAVE_TX  8

// If not defined - use as Master Transmitter only!
// Если нет ничего, то только управляющий мастер!
#ifndef TWI_ON
#  define TWI_ON TWI_MASTER_TX
#endif // TWI_ON

// ------------ All states TWI status register AND twiState: ------------- //
// Misc
#define TWI_ERROR                  0x00 // Misc: illegal start or stop condition
#define TWI_NO_INFO                0xF8 // Misc: no state information available

// I am Master
#define TWI_START                  0x08 // start condition transmitted
#define TWI_REP_START              0x10 // repeated start condition transmitted
#define TWI_MTR_ARB_LOST           0x38 // arbitration lost in SLA+W or data

// Master Transmitter
#define TWI_MT_SLA_ACK             0x18 // address: SLA+W transmitted, ACK received
#define TWI_MT_SLA_NACK            0x20 // address: SLA+W transmitted, NACK received
#define TWI_MT_DATA_ACK            0x28 // data: transmitted, ACK received
#define TWI_MT_DATA_NACK           0x30 // data: transmitted, NACK received

// Master Receiver
#define TWI_MR_SLA_ACK             0x40 // address: SLA+R transmitted, ACK received
#define TWI_MR_SLA_NACK            0x48 // address: SLA+R transmitted, NACK received
#define TWI_MR_DATA_ACK            0x50 // data: received, ACK returned
#define TWI_MR_DATA_NACK           0x58 // data: received, NACK returned

// I am Slave
// Slave Receiver
#define TWI_SR_SLA_ACK             0x60 // address: SLA+W received, ACK returned
#define TWI_SR_ARB_LOST_SLA_ACK    0x68 // arbitration lost in SLA+RW, SLA+W received, ACK returned
#define TWI_SR_GCALL_ACK           0x70 // general call received, ACK returned
#define TWI_SR_ARB_LOST_GCALL_ACK  0x78 // arbitration lost in SLA+RW, general call received, ACK returned
#define TWI_SR_DATA_ACK            0x80 // data: received, ACK returned
#define TWI_SR_DATA_NACK           0x88 // data: received, NACK returned
#define TWI_SR_GCALL_DATA_ACK      0x90 // general call data received, ACK returned
#define TWI_SR_GCALL_DATA_NACK     0x98 // general call data received, NACK returned
#define TWI_SR_STOP                0xA0 // stop or repeated start condition received while selected

// Slave Transmitter
#define TWI_ST_SLA_ACK             0xA8 // address: SLA+R received, ACK returned
#define TWI_ST_ARB_LOST_SLA_ACK    0xB0 // arbitration lost in SLA+RW, SLA+R received, ACK returned
#define TWI_ST_DATA_ACK            0xB8 // data: transmitted, ACK received
#define TWI_ST_DATA_NACK           0xC0 // data: transmitted, NACK received
#define TWI_ST_LAST_DATA           0xC8 // last data byte transmitted, ACK received

// ------------ Macros for TWI ------------- //

#define TWI_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) // Two LSB are prescaler bits
#define TWI_STATUS      (TWSR & TWI_STATUS_MASK)                            // Get status from TWSR

#define TWI_READ    1                   // for SLA+R address
#define TWI_WRITE   0                   // SLA+W address
#define TWI_ACK     1
#define TWI_NACK    0

#define twiOn()       (PRR0 &= ~_BV(7)) // =0: TWI power is On (default on power!)
#define twiOff()      (PRR0 |= _BV(7))  // =1: TWI power is Off

/**
 * Set twi bit rate and prescaler: _twbr:[0..255], _twsr:[0..3]
 * ------------------------------------------------------------
 * _twsr = 3: scl freq. = [   490 .. 111_111] hz
 * _twsr = 2: scl freq. = [ 1_960 .. 333_333] hz
 * _twsr = 1: scl freq. = [ 7_782 .. 666_667] hz
 * _twsr = 0: scl freq. = [30_418 .. 888_889] hz
 * IF _twbr = 0: frequency equal 1 Mhz with any _twsr!
 *
 * @example twiSetRate(72,0) : use 100_000hz standart mode
 * @example twiSetRate(12,0) : use 400_000hz standart mode
 * @example twiSetRate(0,0)  : use 1 Mhz mode
 */
#define twiSetRate(_twbr, _twsr)      \
{                                     \
  _SFR_BYTE(TWSR) = (uint8_t)(_twsr); \
  TWBR = (uint8_t)(_twbr);            \
}
/**
 * Макросы управления автоматом TWI: последняя команда везде - запись в TWCR
 *
 * @param bool ack -- есть ли режимы Slave, надо ли слушать шину?
 *
 * twiStart()      -- включаем, разрешаем, сброс прерывание(запуск КА) и выставляем старт на шину.
 * twiReply()      -- оно же, только без старта
 * twiReleaseBus() -- включаем, сброс прерывание(запуск КА), а вот прерывание разрешаем только если есть Slave
 * twiStop()       -- оно же, только ещё и отправляем "стоп" в шину и ждем исполнения.
 */
#define twiStart(ack)                   (_BV(TWEN)|_BV(TWINT)|_BV(TWIE)|_BV(TWSTA)|((ack)?_BV(TWEA):0))
#define twiReply(ack)                   (_BV(TWEN)|_BV(TWINT)|_BV(TWIE)|((ack)? _BV(TWEA):0))
#define twiReleaseBus(ack)              (_BV(TWEN)|_BV(TWINT)|((ack)? _BV(TWEA)|_BV(TWIE):0))
#define twiSetAddress(address, isGcall) (TWAR  = (uint8_t)(((address) << 1)|((isGcall)&0x01)))
#define twiSetMaskAddress(mask)         (TWAMR = (uint8_t)(mask))
#define twiStop(ack)                    \
{                                       \
  TWCR = _BV(TWSTO)|twiReleaseBus(ack); \
  while(TWCR & _BV(TWSTO));             \
  twiMode |= TWI_READY;                 \
}

#ifdef __cplusplus
    extern "C" {
#endif
// ------------ TWI internal variables ------------- //

enum TwiModes {
     TWI_IS_SLAVE  = 1                                  // have I slave mode too?
    ,TWI_SEND_STOP = 2                                  // is need send stop when Master is ending?
    ,TWI_READY     = 4                                  // previous work is ended
};

volatile uint8_t    twiMode;
volatile uint8_t    twiState;                           // state TWI automat
volatile uint8_t    twiSLARW;                           // address for send to (SLARW)

volatile uint8_t   twiMT_Count;                         // остаток байт для передачи мастеров
volatile uint8_t  * twiMT_Ptr;                          // указатель текущего байта внешнего буфера передачи мастером

volatile uint8_t   twiRX_Count;                         // остаток байт для приема мастером/слейвом
volatile uint8_t  * twiRX_Ptr;                          // указатель текущего байта внешнего буфера приема мастером/слейвом

volatile uint8_t   twiST_Count;                         // остаток байт для передачи слейвом
volatile uint8_t  * twiST_Ptr;                          // указатель текущего байта внешнего буфера передачи слейвом

volatile void    (* twiHookRestart)(void) = 0;          // указатель на функцию перезапуска мастера без освобождения шины (TWI_SEND_STOP)
volatile void    (* twiMasterReader)(void) = 0;         // указатель на функцию "Master принял данные, куда их?"
volatile void    (* twiSlaveReader)(void) = 0;          // указатель на функцию "Slave принял данные, куда их?"
volatile void    (* twiSlaveWriter)(void) = 0;          // указатель на функцию "Slave всё отправил, что дальше?"

/*
#if defined(TWI_ON) && (TWI_ON & TWI_LOG_ON)

typedef struct {
    uint16_t   starts,restarts,stops,losts,noslarw,mtx,mrx,srx,grx,stx;
} TwiStat;
#define ptrTwiStat(ptr)  ((TWI_Stat *)(ptr))
static volatile TwiStat    twiStatistic;

#endif // TWI_ON::TWI_LOG_ON
*/
// ------------ TWI functions ------------- //

/**
 * Autocalculate and set twi prescaler and bit rate
 * 1Mhz      .. 30.418khz : TWSR=0!
 * 30.42khz  ..  7.782khz : TWSR=1
 *  7.782khz ..  1.960khz : TWSR=2
 *  1/960khz ..  0.490khz : TWSR=3
 */
void twiSpeed(uint32_t freq)
{
    uint16_t bitRate = (F_CPU / freq) - 16;
    uint8_t  bitMul  = 0;

    while( (bitRate > 511) && (bitMul < 3) ){
        bitRate /= 4; bitRate += 1; bitMul++;
    }
    bitRate /= 2;
    if( bitRate > 255 ) return;
    twiSetRate(bitRate, bitMul);
}

/**
 * for Arduino setup() as Master or Slave or Both modes
 * freq:[490 .. 1 000 000], mode:[0,TWI_IS_SLAVE]
 */
void twiSetup(uint32_t freq, uint8_t mode)
{
    digitalWrite(I2C_SDA, HIGH);                                  // internal pullup is ON.
    digitalWrite(I2C_SCL, HIGH);
    twiSpeed(freq);                                               // set bitrate and prescaler for frequency
    twiMode = mode;
    TWCR = _BV(TWEN)|_BV(TWIE)|((mode&TWI_IS_SLAVE)?_BV(TWEA):0); // module, acks, and interrupt is ON
}

/**
 * for ISR(TWI): control restart conditions in all modes:
 * ! if only 1 mode -- this is a MACRO with next RETURN into ISR, else - function !
 *
 * 1. Освобождать шину, или надо ещё (напр. прием после передачи)?
 * да: Сеанс завершен. Ждем прямо тут прохождения стопа! Выходим из обработчика тут!
 * нет, рестарт:
 * .. есть Хук? процедура подготовки след. посылки: указатели, размеры, адрес, режим..
 * .. а нет Хука! Типовой режим "чтение после записи"
 * в любом случае отправляем restart
 *
 */
#if ((TWI_ON & 0x0F)!=1) && ((TWI_ON & 0x0F)!=2) && ((TWI_ON & 0x0F)!=4) && ((TWI_ON & 0x0F)!=8)
void twiSendStop(uint8_t _md)
{
    if (_md & TWI_SEND_STOP)
    {
        TWCR = _BV(TWSTO)|twiReleaseBus(_md & TWI_IS_SLAVE);
        while(TWCR & _BV(TWSTO));
        twiMode |= TWI_READY;
    }else{
        if( twiHookRestart ){
            twiHookRestart();
        } else {
            twiSLARW |= TWI_READ;
        }
        TWCR = twiStart(_md & TWI_IS_SLAVE);
    }
}
#else
#define twiSendStop(_md)                                   \
{                                                          \
    TWCR = _BV(TWSTO)|twiReleaseBus((_md) & TWI_IS_SLAVE); \
    while(TWCR & _BV(TWSTO));                              \
    twiMode |= TWI_READY;                                  \
}
#endif

/**
 * ISR for TWI interface: realised master and slave modes
 * ------------------------------------------------------
 */
ISR(TWI_vect)
{
    uint8_t _cr = twiReply(0);
    uint8_t _md = twiMode;
    uint8_t _st = twiState=TWI_STATUS;

#if defined(TWI_ON) && ((TWI_ON & TWI_IS_SLAVE_TX)||(TWI_ON & TWI_IS_SLAVE_RX))
    if( _st >= TWI_SR_SLA_ACK )
    {
#if (TWI_ON & TWI_IS_SLAVE_TX)
        if( (_st == TWI_ST_DATA_NACK) || (_st == TWI_ST_LAST_DATA) )
        {
            // ST: Был последний байт, мастер наелся ..
            // ST: Был наш последний байт: предупреждали мастера twiReply(NACK)
            twiSlaveWriter();                                   // Хук - обязателен! Дальше нечего передавать..
            _md=twiMode;                                        // возможно изменение режимов в хуке!
            twiSendStop(_md); return;
        }else{
            if( (_st == TWI_ST_ARB_LOST_SLA_ACK) || (_st == TWI_ST_SLA_ACK || _st == TWI_ST_DATA_ACK) )
            {
                // ST: Моего мастера заткнули и просят данные ..
                // ST: Мой адрес, начинаем ..
                // ST: Отправлено успешно, продолжаем ..
                TWDR = *twiST_Ptr++;
                _cr = twiReply(--twiST_Count);
            }else{
#endif // TWI_ON::TWI_IS_SLAVE_TX
#if (TWI_ON & TWI_IS_SLAVE_RX)
                if( (_st == TWI_SR_GCALL_DATA_NACK) || (_st == TWI_SR_DATA_NACK) || (_st == TWI_SR_STOP) )
                {
                    if( (_st == TWI_SR_GCALL_DATA_NACK) || (_st == TWI_SR_DATA_NACK) )
                    {
                        // SR: УПС. GCall - туда же.
                        // SR: УПС. Получен байт, мастеру уже был отправлен NACK
                        *twiRX_Ptr = TWDR;
                    }
                    // SR: Обнаружен stop или restart в процессе приема .. это всё?
                    twiSlaveReader();                           // Хук обязателен! это последний, дальше некуда складывать!
                    _md=twiMode;                                // возможно изменение режимов в хуке!
                    twiSendStop(_md); return;
                }else{
                    if( (_st == TWI_SR_GCALL_DATA_ACK) || (_st == TWI_SR_DATA_ACK) )
                    {
                        // SR: пришел байт всем - аналогично
                        // SR: пришел байт, можно ещё принять
                        *(twiRX_Ptr++) = TWDR;
                    }
                    // TWI_SR_ARB_LOST_SLA_ACK   SR: Мастер потерял шину: нет данных ..
                    // TWI_SR_ARB_LOST_GCALL_ACK SR: Вызов всем потерял шину (как это?) --""--
                    // TWI_SR_SLA_ACK            SR: Адрес принят, ещё только ждем данные
                    // TWI_SR_GCALL_ACK          SR: Вызов всем принят оно же
                    _cr = twiReply(--twiRX_Count);              // .. приняли байт и отправляем NACK если осталось 1 место.
                }
#endif // TWI_ON::TWI_IS_SLAVE_RX
#if (TWI_ON & TWI_IS_SLAVE_TX)
            }
        }
#endif // TWI_ON::TWI_IS_SLAVE_TX
    }else{

#endif // TWI_ON::TWI_SLAVE..
#if defined(TWI_ON) && (TWI_ON & TWI_MASTER_TX)
        // Master Transmiter or Reciever modes
        if( (_st == TWI_START) || (_st == TWI_REP_START) )
        {
            // MT,MR:: Прошла отправка стартовой посылки
            // MT,MR:: Прошла отправка повторного старта
            TWDR = twiSLARW;
            _cr = twiReply(_md & TWI_IS_SLAVE);
        }else{
            if( (_st == TWI_MT_SLA_NACK) || (_st == TWI_MT_DATA_NACK) )
            {
                // TWI_MT_DATA_NACK MT:: Упс. data NACK: Получатель не хотит?
                // TWI_MT_SLA_NACK  MT:: Упс. Получатель NACK .. не откликается зараза.
                twiSendStop(_md); return;
            }
            if( (_st == TWI_MT_SLA_ACK)  || (_st == TWI_MT_DATA_ACK) )
            {
                // MT: Адрес получателя отправлен успешно, начинаем
                // MT: Байт данных отправлен, продолжаем
                if( twiMT_Count-- ){
                    TWDR = *twiMT_Ptr++;
                    _cr = twiReply(_md & TWI_IS_SLAVE);
                }else{
                    twiSendStop(_md); return;
                }
            }else{
#endif // TWI_ON::TWI_MASTER_TX
#if defined(TWI_ON) && (TWI_ON & TWI_MASTER_RX)
                if( _st == TWI_MTR_ARB_LOST )
                {
                    // MT,MR: Упс. Мастер потерял шину: освобождаем и ждем/слушаем.
                    _md |= TWI_READY;
                    _cr = twiReleaseBus(_md & TWI_IS_SLAVE);
                }else{
                    if( (_st == TWI_MR_DATA_ACK) || (_st == TWI_MR_SLA_ACK) )
                    {
                        if( _st == TWI_MR_DATA_ACK ){
                            // MR: байт принят, ACK отправлен
                            *(twiRX_Ptr++) = TWDR;
                        }
                        // MR: Отправитель найден, начинаем прием
                        _cr = twiReply( --twiRX_Count );        // .. Можно ещё принять? Или Отправителю - NACK
                    }else{
                        if( _st == TWI_ERROR ) return;
                        if( _st == TWI_MR_DATA_NACK )
                        {
                            // MR: Упс. Получен последний байт дальше принимать некуда.
                            *twiRX_Ptr = TWDR;
                            if( twiMasterReader ) twiMasterReader();
//                            goto TWI_RET_HOOK;
                            _md=twiMode;                        // возможно изменение режимов в хуке!
                        }
                        // All other Master states: stop/restart if need
                        // TWI_MR_SLA_NACK  MR:: Упс. Отправитель NACK .. не откликается зараза.
                        twiSendStop(_md); return;
                    }
                }
#endif // TWI_ON::TWI_MASTER_RX
#if defined(TWI_ON) && (TWI_ON&TWI_MASTER_TX)
            }
        }
#endif // TWI_ON::TWI_MASTER_TX
#if defined(TWI_ON) && ((TWI_ON & TWI_IS_SLAVE_TX)||(TWI_ON & TWI_IS_SLAVE_RX))
    }
#endif // TWI_SLAVE_ON
    twiMode = _md;
    TWCR = _cr;
} //end ISR()

// -------------------------              (PROTECTED)             ------------------------- //
// Внутренние методы интерфейса, могут пригодится для создания внешних уровней работы с I2C //
// ---------------------------------------------------------------------------------------- //

/**
 * INTERNAL:: Простая передача. Запись адреса собеседника и запуск автомата TWI
 *
 * @see twiWrite(), twiRead()
 */
void _twiStartTo(uint8_t address)
{
    twiSLARW = address;                                 // Режим поставляется вместе с адресом!
    twiMode |= TWI_SEND_STOP;                           // только 1 бит! Могли быть иные режимы..
    TWCR = twiStart(twiMode & TWI_IS_SLAVE);
}

/**
 * INTERNAL:: Передача с рестартом. Запись адреса собеседника и запуск автомата TWI
 *
 * @see twiWrite(), twiRead()
 */
void _twiStartRe(uint8_t address)
{
    twiSLARW = address;                                 // Режим поставляется вместе с адресом!
    twiMode &= ~TWI_SEND_STOP;                          // только 1 бит! Могли быть иные режимы..
    TWCR = twiStart(twiMode & TWI_IS_SLAVE);
}

#define _twiWaitReady()             {while(!(TWI_READY & twiMode)); twiMode &= ~TWI_READY;}
#define _twiMT_Buffer(data, length) (twiMT_Ptr=(volatile uint8_t *)(data), twiMT_Count=(volatile uint8_t)(length))
#define _twiRX_Buffer(data, length) (twiRX_Ptr=(volatile uint8_t *)(data), twiRX_Count=(volatile uint8_t)(length))
#define _twiST_Buffer(data, length) (twiST_Ptr=(volatile uint8_t *)(data), twiST_Count=(volatile uint8_t)(length))

// -------------------------                PUBLIC                ------------------------- //
// ---------------------------------------------------------------------------------------- //

/**
 * Master-TX:: Передача length байт по адресу получателя. Только запуск!
 */
void twiWrite(uint8_t address, const uint8_t * data, uint8_t length)
{
    _twiWaitReady();                                    // Ждем завершения предыдущей работы
    _twiMT_Buffer(data, length);
    _twiStartTo( (address<<1) | TWI_WRITE );            // Режим передачи!
}

/**
 * MASTER-RX:: Прием length байт из адреса отправителя. Только запуск!
 */
void twiRead(uint8_t address, uint8_t * data, uint8_t length)
{
    _twiWaitReady();                                    // Ждем завершения предыдущей работы
    _twiRX_Buffer(data, length);
    _twiStartTo( (address<<1) | TWI_READ );             // Режим приема данных!
}

/**
 * Master Read-after-Write:: Чтение данных после отправки команды. Только запуск.
 * !!! Не совместимо с Slave Receive Mode - буфер приема общий !!!
 */
void twiRAW(uint8_t address                             // адрес устройства
, uint8_t* command, uint8_t clength                     // команда и её длина
, uint8_t* data, uint8_t dlength                        // буфер приема данных и его длина
){
    _twiWaitReady();                                    // Ждем завершения предыдущей работы
    _twiMT_Buffer(command, clength);
    _twiRX_Buffer(data, dlength);

    twiHookRestart = 0;                                 // типовой переход на чтение этого же Slave
    twiMode |= TWI_SEND_STOP;                           // рестарт после отправки команды
    twiSLARW = (address<<1 | TWI_WRITE);                // Сначала режим передачи!
    TWCR = twiStart(twiMode & TWI_IS_SLAVE);
}

/**
 * @example: Пример реализации обработчика "SLAVE отправил всё, что дальше?"
 * для случая, когда текущий буфер можно отправлять повторно по следующему запросу
 *
 * !!! Использует доп. глобалы (должны быть определены в вашем скетче):
 *  uint8_t * stBuffer;
 *  uint8_t   stBufferSize;
 *//*
 void twiSlaveRewriter(void)
 {
     _twiST_Buffer(stBuffer, stBufferSize);             // просто перенастраиваем буфер на повторную передачу.
 }
*/
#ifdef __cplusplus
    }
#endif

#endif // _ARHAT_TWI_H_

Убрал инклуд arhat.h, он тут вроде бы не нужен. В общем, сам давно не пользовал - требует перепроверки, но должен быть рабочим. Указатели на буфера настраивать самостоятельно в сетапе на свою буферизацию, если вообще требуется. Мне понадобилось только однажды..

P.S. Пример применения и библиотека работы с LCD1602 через этот драйвер:

/**
 * Набор примитивов для работы с дисплеем LCD1602 или иными на базе 1,2 контроллеров HD44780
 * 1 контроллер держит дисплеи 8х2 или 16х1; 2 контроллера держат дисплеи 16х2. Программируются вроде как одинаково.
 *
 * !!! Пока только базовые примимтивы .. в развитии.
 *
 * Тайминги циклов шины связи с МК HD44780:
 * 1. Предустановка RS,R/W:          >60нсек от фронта E (>80нсек х2)
 * 2. Длительность строба записи E:  >500нсек (x2 устр.: LCD1602!) с периодом >1мксек
 * 3. Предустановка данных D7..D0:   >300нсек от спада E
 * 4. Удержание D7..D0, RS, R/W:     >20нсек после спада E (1), >300нсек (x2 контроллера!)
 * 5. Время исполнения команд в среднем <=37мксек -- в даташите НЕВЕРНО! Реально около 4.1 миллисекунды !!!
 *
 * Длительность 1 передачи по I2C на 100кГц = 90мксек! В реальности тянет до 800кгц..
 * Нельзя ничего читать с дисплея (R/W===0)! А ведь есть BF - "бит готовности" ..
 *
 * Отсюда передача возможна только побайтная:
 * сразу готовим посылку каждой тетрады в виде 3-х байт: установка, строб, сброс строба,
 * и сразу же отправляем одной пачкой обе тетрады. Итого буфер на передачу 1 байта = 6.
 *
 * @author Arhat109-20160402. arhat109@mail.ru
 * @license:
 *   1. This is a free software for any using and distributing without any warranties.
 *   2. You should keep author tag with any changes. May be with adding.
 *   Это свободное ПО для любого использования без каких-либо гарантий и претензий.
 *   Требуется сохранять тег @author при любых изменениях. Можете дописать свой.
 */
#ifndef _LCD1602_H_

#ifndef _ARHAT_TWI_H_
  #define TWI_ON           TWI_MASTER_TX         // only Master-Transmit mode! Not use other mode in this!
  #include "arhat_twi.h"
#endif // _ARHAT_TWI_H_

#ifndef LCD_I2C_SPEED
  #define LCD_I2C_SPEED    800000
#endif

// LCD on chip HD44780 команды или их коды операций:
#define LCD_CLEAR          0x01
#define LCD_HOME           0x02
#define LCD_SHIFTS         0x04  // часть!
#define LCD_SHOWS          0x08  // часть!
#define LCD_MODES          0x20  // часть!
#define LCD_FONT_RAM       0x40  // адрес в таблицу шрифтов 6бит
#define LCD_SHOW_RAM       0x80  // адрес текущей позиции 7бит

#define LCD_CURSOR_LEFT    0x10  // команда сдвига курсора влево
#define LCD_CURSOR_RIGHT   0x14  // команда сдвига курсора вправо
#define LCD_ROW_LEFT       0x18  // команда сдвига всей строки влево
#define LCD_ROW_RIGHT      0x1C  // команда сдвига всей строки вправо

// for LCD_SHIFTS:
#define LCD_INC            0x02  // Инкремент: сдвиг вправо при записи байта
#define LCD_SHIFT_ON       0x01  // сдвиг и строки тоже при записи байта (в обратную сторону! Курсор как-бы на месте)

// for LCD_SHOWS:
#define LCD_SHOW_ON        0x04  // включить отображение
#define LCD_CURSOR_UL      0x02  // включить курсор подчерком
#define LCD_CURSOR_BLINK   0x01  // включить мигание курсора

// for LCD_MODES:
#define LCD_8BIT           0x10  // шина 8бит/4бит
#define LCD_2LINE          0x08  // память 2/1 строки
#define LCD_5x10           0x04  // фонт 5х10 / 5х8 точек

// Пустышки для полноты картины:
#define LCD_DEC        0 // сдвиг курсора/экрана влево
#define LCD_SHIFT_OFF  0 // сдвиг строки отключен
#define LCD_SHOW_OFF   0 // дисплей выключен
#define LCD_CURSOR_OFF 0 // курсор выключен
#define LCD_5x8        0
#define LCD_1LINE      0 // только одна строка (1 буфер 80символов, иначе 2х40)
#define LCD_4BIT       0 // 4бита: каждый байт идет 2 посылками "подряд" по линиям D7..D4

// Маски бит данных (D3,D2,D1,D0), управляющие сигналами напрямую:
#define LCD_BACK      B00001000  // bit D3 управляет подсветкой экрана при каждом выводе в I2C!
#define LCD_E         B00000100  // Enable bit - Strobe for read/write >230msec.
#define LCD_RW        B00000010  // Read/Write bit - ==0 всегда!
#define LCD_RS        B00000001  // Команда(0) или данные(1)

#define LCD_WAIT_BOOT0       15  // тиков Т0:  >15мсек пауза на включение
#define LCD_WAIT_BOOT1        5  // тиков Т0:  >4,1мсек пауза повторной 0x30
#define LCD_WAIT_BOOT2      400  // (по 250нсек) >100мксек пауза третьей 0x30
#define LCD_WAIT_1            5  // тиков Т0: 4мксек - мало! (>37мксек пауза на команду "в среднем")

/**
 * Отправка буфера дисплею по 4-битному протоколу с "ручным" стробированием
 * и типовой задержкой на выполнение команды
 */
#define lcdSend(len)                               \
{                                                  \
  twiWrite(lcdAddress, lcdBuffer, (uint8_t)(len)); \
  delay(LCD_WAIT_1);                               \
}

#define lcdWrite1(d)     {lcdPrepare((uint8_t)(d), 1); lcdSend(6);}
#define lcdCommand(p)    {lcdPrepare((uint8_t)(p), 0); lcdSend(6);}
#define lcdClear()       {lcdCommand(LCD_CLEAR); delay(16);}
#define lcdHome()         lcdCommand(LCD_HOME)
#define lcdCursorLeft()   lcdCommand(LCD_CURSOR_LEFT)
#define lcdCursorRight()  lcdCommand(LCD_CURSOR_RIGHT)
#define lcdRowLeft()      lcdCommand(LCD_ROW_LEFT)
#define lcdRowRight()     lcdCommand(LCD_ROW_RIGHT)
#define lcdFontAddress(f) lcdCommand(LCD_FONT_RAM | ((uint8_t)(f)&0x3F))
#define lcdShowAddress(a) lcdCommand(LCD_SHOW_RAM | ((uint8_t)(a)&0x7F))

/**
 * Установить абсолютную позицию курсора
 */
#define lcdSetCursor(_col,_row) \
    (lcdCommand(LCD_SHOW_RAM | ((((_row)? 0x40 : 0x00) + (uint8_t)(_col)) & 0x7f)))

/**
 * Установить позицию записи в память шрифтов
 */
#define lcdGoChar5x8(_ch) (lcdCommand(LCD_FONT_RAM | ((((uint8_t)(_ch))<<3)&0x3f)))

#ifdef __cplusplus
  extern "C" {
#endif

    uint8_t lcdModes  = LCD_MODES | LCD_8BIT;               // PowerON: 8bit mode, 1 line, 5x8 font
    uint8_t lcdShifts = LCD_SHIFTS | LCD_INC;               // PowerON: cursor shift to right, not screen!
    uint8_t lcdShows  = LCD_SHOWS;                          // PowerON: show off, cursor off, blink off
    uint8_t lcdBackLight = LCD_BACK;                        // PowerON: Backlight is ON

    uint8_t lcdAddress = 0x27;       // for myLCD1602 parameters as default
    uint8_t lcdCols = 16;
    uint8_t lcdRows = 2;
    uint8_t lcdBuffer[6];            // Буфер для потоковой тетрадной записи в дисплей

    /**
     * Подготовка байта в буфер потоковой записи
     * @param _rs=[0 -- команда,!0 -- данные]
     */
    void lcdPrepare(uint8_t _data, uint8_t _rs)
    {
        uint8_t nibble = (_data&0xf0) | lcdBackLight;

        if( _rs ) nibble |= LCD_RS;
        lcdBuffer[2] = lcdBuffer[0] = nibble;
        nibble |= LCD_E;
        lcdBuffer[1] = nibble;

        nibble = ((_data&0x0f)<<4) | lcdBackLight;

        if( _rs ) nibble |= LCD_RS;
        lcdBuffer[5] = lcdBuffer[3] = nibble;
        nibble |= LCD_E;
        lcdBuffer[4] = nibble;
    }

    /**
     * Вывод строки заданной длины (буфера) на экран.
     * Повторная установка скорости работы дисплея по I2C и режима (мало ли кто и как работает ещё)
     */
    void lcdWrite(const void *buf, uint8_t len)
    {
        uint8_t *_b = (uint8_t *)buf;
        uint8_t  _l = len;

        twiMode |= TWI_SEND_STOP;
        while(_l--)
            lcdWrite1(*_b++);
    }

    /**
     * Полная перенастройка экрана по текущим значениям режимов.
     */
    void lcdInit()
    {
        lcdCommand(lcdModes);           // повторяем режим: 4 бита, но уже + сколько строк и какой шрифт
        lcdCommand(lcdShows);           // включаем дисплей и курсор
        lcdCommand(lcdShifts);          // настройка режимов сдвига курсора/экрана
        lcdClear();                     // очень долго очищаем экран (>15.2мсек)
        lcdHome();
    }

    /**
     * for setup(): powerON initialization for LCD with Hitachi HD44780 (up to 40 cols, 2 rows)
     */
    void lcdSetup(uint8_t _address, uint8_t _cols, uint8_t _rows, uint8_t _backLight)
    {
        lcdAddress   = _address;
        lcdCols      = _cols;
        lcdRows      = _rows;
        lcdBackLight = (_backLight? LCD_BACK : 0);
        lcdModes     = LCD_MODES;
        lcdShifts    = LCD_SHIFTS | LCD_INC;
        lcdShows     = LCD_SHOWS  | LCD_SHOW_ON;

        #ifndef TWI_SETUP
        twiSetup(LCD_I2C_SPEED, TWI_READY|TWI_SEND_STOP); // только если не запущен ранее!
        #define TWI_SETUP "lcd1602.h"                     // фиксим, что I2C инициализируется этой библиотекой
        #endif

        if( _rows>1 )          { lcdModes |= LCD_2LINE; } // else 1 line

        // @see datasheet: power on sequence
        {
            lcdPrepare(0x30, 0);          // HD44780: RS=0 запись в регистр команд дисплея.

            delay(LCD_WAIT_BOOT0);        // powerOn it needs wait from 15 upto 50msec
            lcdSend(3);                   // 1x3 отправка: по включению режим 8-бит - вторая тетрада не нужна!
            delay(LCD_WAIT_BOOT1);        // ждем >4.1ms
            lcdSend(3);                   // вторая отправка согласно даташит 8-бит..
            delayMicro16(LCD_WAIT_BOOT2); // ждем >100us
            lcdSend(3);                   // третья отправка 8-бит..

            lcdPrepare(0x20, 0);          // и только теперь переводим в режим 4-бита
            lcdSend(3);                   // и только теперь режим 4-бита и отправляем тетрады попарно!
        }
        lcdInit();
        delay(500);                     // чтобы было заметно глазом.. один раз.
    }

#ifdef __cplusplus
  } // extern "C"
#endif

#endif // _LCD1602_H_

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

Пример работы с LCD1602 оттуда же. Насколько помню, остальные фичи arhat.h тут тоже не нужны.

/**
 * Пример скетча вывода на LCD1602 или аналогичные дисплеи, подключаемые по шине I2C.
 *
 * В стадии развития и тестирования ..
 *
 * Лицензия:
 * 1. Полностью свободное и бесплатное программное обеспечение. В том числе и от претензий.
 * 2. Вы вправе использовать его на свои нужды произвольным образом и на свой риск.
 * 3. Вы не вправе удалять из него строку с тегом @author или изменять её.
 * 4. Изменяя этот файл, Вы вправе дописать свои авторские данные и/или пояснения.
 *
 * Если Вам это оказалось полезным, то Вы можете по-достоинству оценить мой труд
 * "на свое усмотрение" (напр. кинуть денег на телефон "сколько не жалко")
 * @author Arhat109 arhat109@mail.ru, +7-(951)-388-2793
 */
#include "arhat.h"

//#define TWI_ON    15    // Debug: Включить все режимы обработчика I2C
//#include "arhat_twi.h"  // Debug: Подключить явно тут с заданным комплектом режимов

//#define LCD_I2C_SPEED 100000 // Установить типовую скорость (внутри задано 800кгц!)
#include "lcd1602.h"

// ************* Example *********** //

void setup() {
    lcdSetup(0x27, 16, 2, 1);
    lcdWrite("Hello, Arhat!", 13);
    lcdSetCursor(0,1);
    lcdWrite("Bye, Bye..", 10);
}
void loop()  {}

P.S. Пересмотрел код. В twiSetup() надо доопределить 2 константы:

// для Мега 2560 так:
#define I2C_SDA		20
#define I2C_SCL		21
// Для Ардуино НАНО похоже так (проверить нумерацию)
#define I2C_SCL     21
#define I2C_SDA     22

Воткнуть можно куда-нибудь в скетч или заголовочные файлы или использовать в twiSetup() те, что определены в Arduino.

Там же есть вызовы digitalWrite() - будут использованы ардуиновские, соответственно можно заменить на прямую запись в порты.

inspiritus
Онлайн
Зарегистрирован: 17.12.2012

Спасибо за отклик, буду пробовать

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

По большому счету этот код не использует wiring практически никак, и работает самостоятельно. Если ничего не поменялось в правилах компиляции (замена версии компилятора) процедур прерываний - то должно работать "как есть".

Ещё: там есть "protected" функции для реализации своих "надстроек" над интерфейсом. И ещё, неплохо бы заменить delay() в файле работы с дисплеем в макросе lcdSend() или там можно использовать что-то типа yeld() для загрузки проца пока он отправляет байтики медленному устройству или работаете с низкой скоростью.

В целом, реально достигнутая скорость на этой библиотеке при работе с lcd1602 что-то около 95мсек на передачу. А вот "символа" или "строки" уже не помню .. спросите у andriano, он тоже экспериментировал в то время..

Драйвер самого I2C только в режиме "мастер-передатчик" там что-то около 200байт или даже меньше.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ну и как, заработало?

inspiritus
Онлайн
Зарегистрирован: 17.12.2012

Еще не пробовал, вернусь из командировки на следующие выхи попробую.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ап, дабы не утонуло. Результат всё ещё интересен..

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В общем, недождался, проверил сам - в версии 1.8.5 - "полет нормальный": скидал все файлы в одну кучку, дообъявил ноги I2C в начале и доставил из arhat.h макрос delayMicro16()

#define I2C_SDA    20
#define I2C_SCL   21

/**
 * INLINE: 16-bit counter: up to 65535*4 F_CPU for 16Mhz:[0.25 .. 16383.75] mcsec.
 * Короткие задержки по 4 цикла ЦПУ (кратно 250 нсек)
 */
#define delayMicro16(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1 \n\t"       \
       "brne 1b\n\t"          \
       :: "w" (__count)       \
        )
// ============================================= arhat_twi.h ================================================== //
/**
 * Работа с TWI (I2C, 2-Wire) интерфейсом через аппаратные прерывания.
 *
 * ! Аппаратно не бывает одновременно режима Master-RX и Slave-RX - поэтому тут используется ОДИН буфер.
 * ! Нет никаких "проверок", настройка глобалов - обязательна до каждого запуска автомата:
 * Master-Transmitter: twiMT_Ptr, twiMT_Count                  -- откуда и сколько байт свистеть;
 * Master-Receiver:    twiRX_Ptr, twiRX_Count, twiMasterReader -- куда и сколько принять и (опц.)что потом сделать;
 * Slave-Receiver:     twiRX_Ptr, twiRX_Count, twiSlaveReader  -- куда и сколько принять и (обяз.)что сделать;
 * Slave-Transmitter:  twiST_Ptr, twiST_Count, twiSlaveWriter  -- откуда и сколько отдать и (обяз.)что потом сделать;
 *
 * Особенности и отличия от типовых разработок (собрано многое вместе):
 * 1. Возможность одновременной работы как в режиме Master так и в режиме Slave;
 * 2. Внешняя буферизация данных при необходимости. Размер памяти определяется вашей программой, а не тут;
 * 3. Возможность ведения статистики - управляется константами компиляции;
 * 4. Произвольная настройка скорости работы интерфейса от 490гц до 1Мгц с авто вычислением прескалера и регистра скорости
 * 5. Возможность уменьшенной комплиции, если не требуются все режимы сразу.
 *
 * Константы компиляции #define (вставить в скетч перед инклудом этого файла нужное):
 * #define TWI_ON 1  // Master-Transmitter only: компилировать как режим Мастера-передатчика    ,ISR(TWI)=190 байт
 * #define TWI_ON 2  // Master-Receiver only:    компилировать как режим Мастер-приемник        ,ISR(TWI)=224 байт
 * #define TWI_ON 4  // Slave-Receiver only:     компилировать как режим Ожидающий получатель   ,ISR(TWI)=202 байт
 * #define TWI_ON 8  // Slave-Transmitter only:  компилировать как режим Отправитель по запросу ,ISR(TWI)=188 байт
 *
 * !!! Допускается компиляция нескольких режимов одновременно:
 * #define TWI_ON (1+2+4+8) // Включить в компиляцию ВСЕ режимы. ,ISR(TWI)=424 байт.
 *
 * @author Arhat109-20160402. <a href="mailto:arhat109@mail.ru">arhat109@mail.ru</a>
 * @license:
 *   1. This is a free software for any using and distributing without any warranties.
 *   2. You should keep author tag with any changes. May be with adding.
 *   Это свободное ПО для любого использования без каких-либо гарантий и претензий.
 *   Требуется сохранять тег @author при любых изменениях. Можете дописать свой.
 */
#ifndef _ARHAT_TWI_H_
#define _ARHAT_TWI_H_ 1

#define TWI_MASTER_TX    1
#define TWI_MASTER_RX    2
#define TWI_IS_SLAVE_RX  4
#define TWI_IS_SLAVE_TX  8

// If not defined - use as Master Transmitter only!
// Если нет ничего, то только управляющий мастер!
#ifndef TWI_ON
#  define TWI_ON TWI_MASTER_TX
#endif // TWI_ON

// ------------ All states TWI status register AND twiState: ------------- //
// Misc
#define TWI_ERROR                  0x00 // Misc: illegal start or stop condition
#define TWI_NO_INFO                0xF8 // Misc: no state information available

// I am Master
#define TWI_START                  0x08 // start condition transmitted
#define TWI_REP_START              0x10 // repeated start condition transmitted
#define TWI_MTR_ARB_LOST           0x38 // arbitration lost in SLA+W or data

// Master Transmitter
#define TWI_MT_SLA_ACK             0x18 // address: SLA+W transmitted, ACK received
#define TWI_MT_SLA_NACK            0x20 // address: SLA+W transmitted, NACK received
#define TWI_MT_DATA_ACK            0x28 // data: transmitted, ACK received
#define TWI_MT_DATA_NACK           0x30 // data: transmitted, NACK received

// Master Receiver
#define TWI_MR_SLA_ACK             0x40 // address: SLA+R transmitted, ACK received
#define TWI_MR_SLA_NACK            0x48 // address: SLA+R transmitted, NACK received
#define TWI_MR_DATA_ACK            0x50 // data: received, ACK returned
#define TWI_MR_DATA_NACK           0x58 // data: received, NACK returned

// I am Slave
// Slave Receiver
#define TWI_SR_SLA_ACK             0x60 // address: SLA+W received, ACK returned
#define TWI_SR_ARB_LOST_SLA_ACK    0x68 // arbitration lost in SLA+RW, SLA+W received, ACK returned
#define TWI_SR_GCALL_ACK           0x70 // general call received, ACK returned
#define TWI_SR_ARB_LOST_GCALL_ACK  0x78 // arbitration lost in SLA+RW, general call received, ACK returned
#define TWI_SR_DATA_ACK            0x80 // data: received, ACK returned
#define TWI_SR_DATA_NACK           0x88 // data: received, NACK returned
#define TWI_SR_GCALL_DATA_ACK      0x90 // general call data received, ACK returned
#define TWI_SR_GCALL_DATA_NACK     0x98 // general call data received, NACK returned
#define TWI_SR_STOP                0xA0 // stop or repeated start condition received while selected

// Slave Transmitter
#define TWI_ST_SLA_ACK             0xA8 // address: SLA+R received, ACK returned
#define TWI_ST_ARB_LOST_SLA_ACK    0xB0 // arbitration lost in SLA+RW, SLA+R received, ACK returned
#define TWI_ST_DATA_ACK            0xB8 // data: transmitted, ACK received
#define TWI_ST_DATA_NACK           0xC0 // data: transmitted, NACK received
#define TWI_ST_LAST_DATA           0xC8 // last data byte transmitted, ACK received

// ------------ Macros for TWI ------------- //

#define TWI_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) // Two LSB are prescaler bits
#define TWI_STATUS      (TWSR & TWI_STATUS_MASK)                            // Get status from TWSR

#define TWI_READ    1                   // for SLA+R address
#define TWI_WRITE   0                   // SLA+W address
#define TWI_ACK     1
#define TWI_NACK    0

#define twiOn()       (PRR0 &= ~_BV(7)) // =0: TWI power is On (default on power!)
#define twiOff()      (PRR0 |= _BV(7))  // =1: TWI power is Off

/**
 * Set twi bit rate and prescaler: _twbr:[0..255], _twsr:[0..3]
 * ------------------------------------------------------------
 * _twsr = 3: scl freq. = [   490 .. 111_111] hz
 * _twsr = 2: scl freq. = [ 1_960 .. 333_333] hz
 * _twsr = 1: scl freq. = [ 7_782 .. 666_667] hz
 * _twsr = 0: scl freq. = [30_418 .. 888_889] hz
 * IF _twbr = 0: frequency equal 1 Mhz with any _twsr!
 *
 * @example twiSetRate(72,0) : use 100_000hz standart mode
 * @example twiSetRate(12,0) : use 400_000hz standart mode
 * @example twiSetRate(0,0)  : use 1 Mhz mode
 */
#define twiSetRate(_twbr, _twsr)      \
{                                     \
  _SFR_BYTE(TWSR) = (uint8_t)(_twsr); \
  TWBR = (uint8_t)(_twbr);            \
}
/**
 * Макросы управления автоматом TWI: последняя команда везде - запись в TWCR
 *
 * @param bool ack -- есть ли режимы Slave, надо ли слушать шину?
 *
 * twiStart()      -- включаем, разрешаем, сброс прерывание(запуск КА) и выставляем старт на шину.
 * twiReply()      -- оно же, только без старта
 * twiReleaseBus() -- включаем, сброс прерывание(запуск КА), а вот прерывание разрешаем только если есть Slave
 * twiStop()       -- оно же, только ещё и отправляем "стоп" в шину и ждем исполнения.
 */
#define twiStart(ack)                   (_BV(TWEN)|_BV(TWINT)|_BV(TWIE)|_BV(TWSTA)|((ack)?_BV(TWEA):0))
#define twiReply(ack)                   (_BV(TWEN)|_BV(TWINT)|_BV(TWIE)|((ack)? _BV(TWEA):0))
#define twiReleaseBus(ack)              (_BV(TWEN)|_BV(TWINT)|((ack)? _BV(TWEA)|_BV(TWIE):0))
#define twiSetAddress(address, isGcall) (TWAR  = (uint8_t)(((address) << 1)|((isGcall)&0x01)))
#define twiSetMaskAddress(mask)         (TWAMR = (uint8_t)(mask))
#define twiStop(ack)                    \
{                                       \
  TWCR = _BV(TWSTO)|twiReleaseBus(ack); \
  while(TWCR & _BV(TWSTO));             \
  twiMode |= TWI_READY;                 \
}

#ifdef __cplusplus
    extern "C" {
#endif
// ------------ TWI internal variables ------------- //

enum TwiModes {
     TWI_IS_SLAVE  = 1                                  // have I slave mode too?
    ,TWI_SEND_STOP = 2                                  // is need send stop when Master is ending?
    ,TWI_READY     = 4                                  // previous work is ended
};

volatile uint8_t    twiMode;
volatile uint8_t    twiState;                           // state TWI automat
volatile uint8_t    twiSLARW;                           // address for send to (SLARW)

volatile uint8_t   twiMT_Count;                         // остаток байт для передачи мастеров
volatile uint8_t  * twiMT_Ptr;                          // указатель текущего байта внешнего буфера передачи мастером

volatile uint8_t   twiRX_Count;                         // остаток байт для приема мастером/слейвом
volatile uint8_t  * twiRX_Ptr;                          // указатель текущего байта внешнего буфера приема мастером/слейвом

volatile uint8_t   twiST_Count;                         // остаток байт для передачи слейвом
volatile uint8_t  * twiST_Ptr;                          // указатель текущего байта внешнего буфера передачи слейвом

volatile void    (* twiHookRestart)(void) = 0;          // указатель на функцию перезапуска мастера без освобождения шины (TWI_SEND_STOP)
volatile void    (* twiMasterReader)(void) = 0;         // указатель на функцию "Master принял данные, куда их?"
volatile void    (* twiSlaveReader)(void) = 0;          // указатель на функцию "Slave принял данные, куда их?"
volatile void    (* twiSlaveWriter)(void) = 0;          // указатель на функцию "Slave всё отправил, что дальше?"

/*
#if defined(TWI_ON) && (TWI_ON & TWI_LOG_ON)

typedef struct {
    uint16_t   starts,restarts,stops,losts,noslarw,mtx,mrx,srx,grx,stx;
} TwiStat;
#define ptrTwiStat(ptr)  ((TWI_Stat *)(ptr))
static volatile TwiStat    twiStatistic;

#endif // TWI_ON::TWI_LOG_ON
*/
// ------------ TWI functions ------------- //

/**
 * Autocalculate and set twi prescaler and bit rate
 * 1Mhz      .. 30.418khz : TWSR=0!
 * 30.42khz  ..  7.782khz : TWSR=1
 *  7.782khz ..  1.960khz : TWSR=2
 *  1/960khz ..  0.490khz : TWSR=3
 */
void twiSpeed(uint32_t freq)
{
    uint16_t bitRate = (F_CPU / freq) - 16;
    uint8_t  bitMul  = 0;

    while( (bitRate > 511) && (bitMul < 3) ){
        bitRate /= 4; bitRate += 1; bitMul++;
    }
    bitRate /= 2;
    if( bitRate > 255 ) return;
    twiSetRate(bitRate, bitMul);
}

/**
 * for Arduino setup() as Master or Slave or Both modes
 * freq:[490 .. 1 000 000], mode:[0,TWI_IS_SLAVE]
 */
void twiSetup(uint32_t freq, uint8_t mode)
{
    digitalWrite(I2C_SDA, HIGH);                                  // internal pullup is ON.
    digitalWrite(I2C_SCL, HIGH);
    twiSpeed(freq);                                               // set bitrate and prescaler for frequency
    twiMode = mode;
    TWCR = _BV(TWEN)|_BV(TWIE)|((mode&TWI_IS_SLAVE)?_BV(TWEA):0); // module, acks, and interrupt is ON
}

/**
 * for ISR(TWI): control restart conditions in all modes:
 * ! if only 1 mode -- this is a MACRO with next RETURN into ISR, else - function !
 *
 * 1. Освобождать шину, или надо ещё (напр. прием после передачи)?
 * да: Сеанс завершен. Ждем прямо тут прохождения стопа! Выходим из обработчика тут!
 * нет, рестарт:
 * .. есть Хук? процедура подготовки след. посылки: указатели, размеры, адрес, режим..
 * .. а нет Хука! Типовой режим "чтение после записи"
 * в любом случае отправляем restart
 *
 */
#if ((TWI_ON & 0x0F)!=1) && ((TWI_ON & 0x0F)!=2) && ((TWI_ON & 0x0F)!=4) && ((TWI_ON & 0x0F)!=8)
void twiSendStop(uint8_t _md)
{
    if (_md & TWI_SEND_STOP)
    {
        TWCR = _BV(TWSTO)|twiReleaseBus(_md & TWI_IS_SLAVE);
        while(TWCR & _BV(TWSTO));
        twiMode |= TWI_READY;
    }else{
        if( twiHookRestart ){
            twiHookRestart();
        } else {
            twiSLARW |= TWI_READ;
        }
        TWCR = twiStart(_md & TWI_IS_SLAVE);
    }
}
#else
#define twiSendStop(_md)                                   \
{                                                          \
    TWCR = _BV(TWSTO)|twiReleaseBus((_md) & TWI_IS_SLAVE); \
    while(TWCR & _BV(TWSTO));                              \
    twiMode |= TWI_READY;                                  \
}
#endif

/**
 * ISR for TWI interface: realised master and slave modes
 * ------------------------------------------------------
 */
ISR(TWI_vect)
{
    uint8_t _cr = twiReply(0);
    uint8_t _md = twiMode;
    uint8_t _st = twiState=TWI_STATUS;

#if defined(TWI_ON) && ((TWI_ON & TWI_IS_SLAVE_TX)||(TWI_ON & TWI_IS_SLAVE_RX))
    if( _st >= TWI_SR_SLA_ACK )
    {
#if (TWI_ON & TWI_IS_SLAVE_TX)
        if( (_st == TWI_ST_DATA_NACK) || (_st == TWI_ST_LAST_DATA) )
        {
            // ST: Был последний байт, мастер наелся ..
            // ST: Был наш последний байт: предупреждали мастера twiReply(NACK)
            twiSlaveWriter();                                   // Хук - обязателен! Дальше нечего передавать..
            _md=twiMode;                                        // возможно изменение режимов в хуке!
            twiSendStop(_md); return;
        }else{
            if( (_st == TWI_ST_ARB_LOST_SLA_ACK) || (_st == TWI_ST_SLA_ACK || _st == TWI_ST_DATA_ACK) )
            {
                // ST: Моего мастера заткнули и просят данные ..
                // ST: Мой адрес, начинаем ..
                // ST: Отправлено успешно, продолжаем ..
                TWDR = *twiST_Ptr++;
                _cr = twiReply(--twiST_Count);
            }else{
#endif // TWI_ON::TWI_IS_SLAVE_TX
#if (TWI_ON & TWI_IS_SLAVE_RX)
                if( (_st == TWI_SR_GCALL_DATA_NACK) || (_st == TWI_SR_DATA_NACK) || (_st == TWI_SR_STOP) )
                {
                    if( (_st == TWI_SR_GCALL_DATA_NACK) || (_st == TWI_SR_DATA_NACK) )
                    {
                        // SR: УПС. GCall - туда же.
                        // SR: УПС. Получен байт, мастеру уже был отправлен NACK
                        *twiRX_Ptr = TWDR;
                    }
                    // SR: Обнаружен stop или restart в процессе приема .. это всё?
                    twiSlaveReader();                           // Хук обязателен! это последний, дальше некуда складывать!
                    _md=twiMode;                                // возможно изменение режимов в хуке!
                    twiSendStop(_md); return;
                }else{
                    if( (_st == TWI_SR_GCALL_DATA_ACK) || (_st == TWI_SR_DATA_ACK) )
                    {
                        // SR: пришел байт всем - аналогично
                        // SR: пришел байт, можно ещё принять
                        *(twiRX_Ptr++) = TWDR;
                    }
                    // TWI_SR_ARB_LOST_SLA_ACK   SR: Мастер потерял шину: нет данных ..
                    // TWI_SR_ARB_LOST_GCALL_ACK SR: Вызов всем потерял шину (как это?) --""--
                    // TWI_SR_SLA_ACK            SR: Адрес принят, ещё только ждем данные
                    // TWI_SR_GCALL_ACK          SR: Вызов всем принят оно же
                    _cr = twiReply(--twiRX_Count);              // .. приняли байт и отправляем NACK если осталось 1 место.
                }
#endif // TWI_ON::TWI_IS_SLAVE_RX
#if (TWI_ON & TWI_IS_SLAVE_TX)
            }
        }
#endif // TWI_ON::TWI_IS_SLAVE_TX
    }else{

#endif // TWI_ON::TWI_SLAVE..
#if defined(TWI_ON) && (TWI_ON & TWI_MASTER_TX)
        // Master Transmiter or Reciever modes
        if( (_st == TWI_START) || (_st == TWI_REP_START) )
        {
            // MT,MR:: Прошла отправка стартовой посылки
            // MT,MR:: Прошла отправка повторного старта
            TWDR = twiSLARW;
            _cr = twiReply(_md & TWI_IS_SLAVE);
        }else{
            if( (_st == TWI_MT_SLA_NACK) || (_st == TWI_MT_DATA_NACK) )
            {
                // TWI_MT_DATA_NACK MT:: Упс. data NACK: Получатель не хотит?
                // TWI_MT_SLA_NACK  MT:: Упс. Получатель NACK .. не откликается зараза.
                twiSendStop(_md); return;
            }
            if( (_st == TWI_MT_SLA_ACK)  || (_st == TWI_MT_DATA_ACK) )
            {
                // MT: Адрес получателя отправлен успешно, начинаем
                // MT: Байт данных отправлен, продолжаем
                if( twiMT_Count-- ){
                    TWDR = *twiMT_Ptr++;
                    _cr = twiReply(_md & TWI_IS_SLAVE);
                }else{
                    twiSendStop(_md); return;
                }
            }else{
#endif // TWI_ON::TWI_MASTER_TX
#if defined(TWI_ON) && (TWI_ON & TWI_MASTER_RX)
                if( _st == TWI_MTR_ARB_LOST )
                {
                    // MT,MR: Упс. Мастер потерял шину: освобождаем и ждем/слушаем.
                    _md |= TWI_READY;
                    _cr = twiReleaseBus(_md & TWI_IS_SLAVE);
                }else{
                    if( (_st == TWI_MR_DATA_ACK) || (_st == TWI_MR_SLA_ACK) )
                    {
                        if( _st == TWI_MR_DATA_ACK ){
                            // MR: байт принят, ACK отправлен
                            *(twiRX_Ptr++) = TWDR;
                        }
                        // MR: Отправитель найден, начинаем прием
                        _cr = twiReply( --twiRX_Count );        // .. Можно ещё принять? Или Отправителю - NACK
                    }else{
                        if( _st == TWI_ERROR ) return;
                        if( _st == TWI_MR_DATA_NACK )
                        {
                            // MR: Упс. Получен последний байт дальше принимать некуда.
                            *twiRX_Ptr = TWDR;
                            if( twiMasterReader ) twiMasterReader();
//                            goto TWI_RET_HOOK;
                            _md=twiMode;                        // возможно изменение режимов в хуке!
                        }
                        // All other Master states: stop/restart if need
                        // TWI_MR_SLA_NACK  MR:: Упс. Отправитель NACK .. не откликается зараза.
                        twiSendStop(_md); return;
                    }
                }
#endif // TWI_ON::TWI_MASTER_RX
#if defined(TWI_ON) && (TWI_ON&TWI_MASTER_TX)
            }
        }
#endif // TWI_ON::TWI_MASTER_TX
#if defined(TWI_ON) && ((TWI_ON & TWI_IS_SLAVE_TX)||(TWI_ON & TWI_IS_SLAVE_RX))
    }
#endif // TWI_SLAVE_ON
    twiMode = _md;
    TWCR = _cr;
} //end ISR()

// -------------------------              (PROTECTED)             ------------------------- //
// Внутренние методы интерфейса, могут пригодится для создания внешних уровней работы с I2C //
// ---------------------------------------------------------------------------------------- //

/**
 * INTERNAL:: Простая передача. Запись адреса собеседника и запуск автомата TWI
 *
 * @see twiWrite(), twiRead()
 */
void _twiStartTo(uint8_t address)
{
    twiSLARW = address;                                 // Режим поставляется вместе с адресом!
    twiMode |= TWI_SEND_STOP;                           // только 1 бит! Могли быть иные режимы..
    TWCR = twiStart(twiMode & TWI_IS_SLAVE);
}

/**
 * INTERNAL:: Передача с рестартом. Запись адреса собеседника и запуск автомата TWI
 *
 * @see twiWrite(), twiRead()
 */
void _twiStartRe(uint8_t address)
{
    twiSLARW = address;                                 // Режим поставляется вместе с адресом!
    twiMode &= ~TWI_SEND_STOP;                          // только 1 бит! Могли быть иные режимы..
    TWCR = twiStart(twiMode & TWI_IS_SLAVE);
}

#define _twiWaitReady()             {while(!(TWI_READY & twiMode)); twiMode &= ~TWI_READY;}
#define _twiMT_Buffer(data, length) (twiMT_Ptr=(volatile uint8_t *)(data), twiMT_Count=(volatile uint8_t)(length))
#define _twiRX_Buffer(data, length) (twiRX_Ptr=(volatile uint8_t *)(data), twiRX_Count=(volatile uint8_t)(length))
#define _twiST_Buffer(data, length) (twiST_Ptr=(volatile uint8_t *)(data), twiST_Count=(volatile uint8_t)(length))

// -------------------------                PUBLIC                ------------------------- //
// ---------------------------------------------------------------------------------------- //

/**
 * Master-TX:: Передача length байт по адресу получателя. Только запуск!
 */
void twiWrite(uint8_t address, const uint8_t * data, uint8_t length)
{
    _twiWaitReady();                                    // Ждем завершения предыдущей работы
    _twiMT_Buffer(data, length);
    _twiStartTo( (address<<1) | TWI_WRITE );            // Режим передачи!
}

/**
 * MASTER-RX:: Прием length байт из адреса отправителя. Только запуск!
 */
void twiRead(uint8_t address, uint8_t * data, uint8_t length)
{
    _twiWaitReady();                                    // Ждем завершения предыдущей работы
    _twiRX_Buffer(data, length);
    _twiStartTo( (address<<1) | TWI_READ );             // Режим приема данных!
}

/**
 * Master Read-after-Write:: Чтение данных после отправки команды. Только запуск.
 * !!! Не совместимо с Slave Receive Mode - буфер приема общий !!!
 */
void twiRAW(uint8_t address                             // адрес устройства
, uint8_t* command, uint8_t clength                     // команда и её длина
, uint8_t* data, uint8_t dlength                        // буфер приема данных и его длина
){
    _twiWaitReady();                                    // Ждем завершения предыдущей работы
    _twiMT_Buffer(command, clength);
    _twiRX_Buffer(data, dlength);

    twiHookRestart = 0;                                 // типовой переход на чтение этого же Slave
    twiMode |= TWI_SEND_STOP;                           // рестарт после отправки команды
    twiSLARW = (address<<1 | TWI_WRITE);                // Сначала режим передачи!
    TWCR = twiStart(twiMode & TWI_IS_SLAVE);
}

/**
 * @example: Пример реализации обработчика "SLAVE отправил всё, что дальше?"
 * для случая, когда текущий буфер можно отправлять повторно по следующему запросу
 *
 * !!! Использует доп. глобалы (должны быть определены в вашем скетче):
 *  uint8_t * stBuffer;
 *  uint8_t   stBufferSize;
 *//*
 void twiSlaveRewriter(void)
 {
     _twiST_Buffer(stBuffer, stBufferSize);             // просто перенастраиваем буфер на повторную передачу.
 }
*/
#ifdef __cplusplus
    }
#endif

#endif // _ARHAT_TWI_H_

// =========================================== LCD1602.h ================================================ //
/**
 * Набор примитивов для работы с дисплеем LCD1602 или иными на базе 1,2 контроллеров HD44780
 * 1 контроллер держит дисплеи 8х2 или 16х1; 2 контроллера держат дисплеи 16х2. Программируются вроде как одинаково.
 *
 * !!! Пока только базовые примимтивы .. в развитии.
 *
 * Тайминги циклов шины связи с МК HD44780:
 * 1. Предустановка RS,R/W:          >60нсек от фронта E (>80нсек х2)
 * 2. Длительность строба записи E:  >500нсек (x2 устр.: LCD1602!) с периодом >1мксек
 * 3. Предустановка данных D7..D0:   >300нсек от спада E
 * 4. Удержание D7..D0, RS, R/W:     >20нсек после спада E (1), >300нсек (x2 контроллера!)
 * 5. Время исполнения команд в среднем <=37мксек -- в даташите НЕВЕРНО! Реально около 4.1 миллисекунды !!!
 *
 * Длительность 1 передачи по I2C на 100кГц = 90мксек! В реальности тянет до 800кгц..
 * Нельзя ничего читать с дисплея (R/W===0)! А ведь есть BF - "бит готовности" ..
 *
 * Отсюда передача возможна только побайтная:
 * сразу готовим посылку каждой тетрады в виде 3-х байт: установка, строб, сброс строба,
 * и сразу же отправляем одной пачкой обе тетрады. Итого буфер на передачу 1 байта = 6.
 *
 * @author Arhat109-20160402. <a href="mailto:arhat109@mail.ru">arhat109@mail.ru</a>
 * @license:
 *   1. This is a free software for any using and distributing without any warranties.
 *   2. You should keep author tag with any changes. May be with adding.
 *   Это свободное ПО для любого использования без каких-либо гарантий и претензий.
 *   Требуется сохранять тег @author при любых изменениях. Можете дописать свой.
 */
#ifndef _LCD1602_H_

#ifndef _ARHAT_TWI_H_
  #define TWI_ON           TWI_MASTER_TX         // only Master-Transmit mode! Not use other mode in this!
  #include "arhat_twi.h"
#endif // _ARHAT_TWI_H_

#ifndef LCD_I2C_SPEED
  #define LCD_I2C_SPEED    800000
#endif

// LCD on chip HD44780 команды или их коды операций:
#define LCD_CLEAR          0x01
#define LCD_HOME           0x02
#define LCD_SHIFTS         0x04  // часть!
#define LCD_SHOWS          0x08  // часть!
#define LCD_MODES          0x20  // часть!
#define LCD_FONT_RAM       0x40  // адрес в таблицу шрифтов 6бит
#define LCD_SHOW_RAM       0x80  // адрес текущей позиции 7бит

#define LCD_CURSOR_LEFT    0x10  // команда сдвига курсора влево
#define LCD_CURSOR_RIGHT   0x14  // команда сдвига курсора вправо
#define LCD_ROW_LEFT       0x18  // команда сдвига всей строки влево
#define LCD_ROW_RIGHT      0x1C  // команда сдвига всей строки вправо

// for LCD_SHIFTS:
#define LCD_INC            0x02  // Инкремент: сдвиг вправо при записи байта
#define LCD_SHIFT_ON       0x01  // сдвиг и строки тоже при записи байта (в обратную сторону! Курсор как-бы на месте)

// for LCD_SHOWS:
#define LCD_SHOW_ON        0x04  // включить отображение
#define LCD_CURSOR_UL      0x02  // включить курсор подчерком
#define LCD_CURSOR_BLINK   0x01  // включить мигание курсора

// for LCD_MODES:
#define LCD_8BIT           0x10  // шина 8бит/4бит
#define LCD_2LINE          0x08  // память 2/1 строки
#define LCD_5x10           0x04  // фонт 5х10 / 5х8 точек

// Пустышки для полноты картины:
#define LCD_DEC        0 // сдвиг курсора/экрана влево
#define LCD_SHIFT_OFF  0 // сдвиг строки отключен
#define LCD_SHOW_OFF   0 // дисплей выключен
#define LCD_CURSOR_OFF 0 // курсор выключен
#define LCD_5x8        0
#define LCD_1LINE      0 // только одна строка (1 буфер 80символов, иначе 2х40)
#define LCD_4BIT       0 // 4бита: каждый байт идет 2 посылками "подряд" по линиям D7..D4

// Маски бит данных (D3,D2,D1,D0), управляющие сигналами напрямую:
#define LCD_BACK      B00001000  // bit D3 управляет подсветкой экрана при каждом выводе в I2C!
#define LCD_E         B00000100  // Enable bit - Strobe for read/write >230msec.
#define LCD_RW        B00000010  // Read/Write bit - ==0 всегда!
#define LCD_RS        B00000001  // Команда(0) или данные(1)

#define LCD_WAIT_BOOT0       15  // тиков Т0:  >15мсек пауза на включение
#define LCD_WAIT_BOOT1        5  // тиков Т0:  >4,1мсек пауза повторной 0x30
#define LCD_WAIT_BOOT2      400  // (по 250нсек) >100мксек пауза третьей 0x30
#define LCD_WAIT_1            5  // тиков Т0: 4мксек - мало! (>37мксек пауза на команду "в среднем")

/**
 * Отправка буфера дисплею по 4-битному протоколу с "ручным" стробированием
 * и типовой задержкой на выполнение команды
 */
#define lcdSend(len)                               \
{                                                  \
  twiWrite(lcdAddress, lcdBuffer, (uint8_t)(len)); \
  delay(LCD_WAIT_1);                               \
}

#define lcdWrite1(d)     {lcdPrepare((uint8_t)(d), 1); lcdSend(6);}
#define lcdCommand(p)    {lcdPrepare((uint8_t)(p), 0); lcdSend(6);}
#define lcdClear()       {lcdCommand(LCD_CLEAR); delay(16);}
#define lcdHome()         lcdCommand(LCD_HOME)
#define lcdCursorLeft()   lcdCommand(LCD_CURSOR_LEFT)
#define lcdCursorRight()  lcdCommand(LCD_CURSOR_RIGHT)
#define lcdRowLeft()      lcdCommand(LCD_ROW_LEFT)
#define lcdRowRight()     lcdCommand(LCD_ROW_RIGHT)
#define lcdFontAddress(f) lcdCommand(LCD_FONT_RAM | ((uint8_t)(f)&0x3F))
#define lcdShowAddress(a) lcdCommand(LCD_SHOW_RAM | ((uint8_t)(a)&0x7F))

/**
 * Установить абсолютную позицию курсора
 */
#define lcdSetCursor(_col,_row) \
    (lcdCommand(LCD_SHOW_RAM | ((((_row)? 0x40 : 0x00) + (uint8_t)(_col)) & 0x7f)))

/**
 * Установить позицию записи в память шрифтов
 */
#define lcdGoChar5x8(_ch) (lcdCommand(LCD_FONT_RAM | ((((uint8_t)(_ch))<<3)&0x3f)))

#ifdef __cplusplus
  extern "C" {
#endif

    uint8_t lcdModes  = LCD_MODES | LCD_8BIT;               // PowerON: 8bit mode, 1 line, 5x8 font
    uint8_t lcdShifts = LCD_SHIFTS | LCD_INC;               // PowerON: cursor shift to right, not screen!
    uint8_t lcdShows  = LCD_SHOWS;                          // PowerON: show off, cursor off, blink off
    uint8_t lcdBackLight = LCD_BACK;                        // PowerON: Backlight is ON

    uint8_t lcdAddress = 0x27;       // for myLCD1602 parameters as default
    uint8_t lcdCols = 16;
    uint8_t lcdRows = 2;
    uint8_t lcdBuffer[6];            // Буфер для потоковой тетрадной записи в дисплей

    /**
     * Подготовка байта в буфер потоковой записи
     * @param _rs=[0 -- команда,!0 -- данные]
     */
    void lcdPrepare(uint8_t _data, uint8_t _rs)
    {
        uint8_t nibble = (_data&0xf0) | lcdBackLight;

        if( _rs ) nibble |= LCD_RS;
        lcdBuffer[2] = lcdBuffer[0] = nibble;
        nibble |= LCD_E;
        lcdBuffer[1] = nibble;

        nibble = ((_data&0x0f)<<4) | lcdBackLight;

        if( _rs ) nibble |= LCD_RS;
        lcdBuffer[5] = lcdBuffer[3] = nibble;
        nibble |= LCD_E;
        lcdBuffer[4] = nibble;
    }

    /**
     * Вывод строки заданной длины (буфера) на экран.
     * Повторная установка скорости работы дисплея по I2C и режима (мало ли кто и как работает ещё)
     */
    void lcdWrite(const void *buf, uint8_t len)
    {
        uint8_t *_b = (uint8_t *)buf;
        uint8_t  _l = len;

        twiMode |= TWI_SEND_STOP;
        while(_l--)
            lcdWrite1(*_b++);
    }

    /**
     * Полная перенастройка экрана по текущим значениям режимов.
     */
    void lcdInit()
    {
        lcdCommand(lcdModes);           // повторяем режим: 4 бита, но уже + сколько строк и какой шрифт
        lcdCommand(lcdShows);           // включаем дисплей и курсор
        lcdCommand(lcdShifts);          // настройка режимов сдвига курсора/экрана
        lcdClear();                     // очень долго очищаем экран (>15.2мсек)
        lcdHome();
    }

    /**
     * for setup(): powerON initialization for LCD with Hitachi HD44780 (up to 40 cols, 2 rows)
     */
    void lcdSetup(uint8_t _address, uint8_t _cols, uint8_t _rows, uint8_t _backLight)
    {
        lcdAddress   = _address;
        lcdCols      = _cols;
        lcdRows      = _rows;
        lcdBackLight = (_backLight? LCD_BACK : 0);
        lcdModes     = LCD_MODES;
        lcdShifts    = LCD_SHIFTS | LCD_INC;
        lcdShows     = LCD_SHOWS  | LCD_SHOW_ON;

        #ifndef TWI_SETUP
        twiSetup(LCD_I2C_SPEED, TWI_READY|TWI_SEND_STOP); // только если не запущен ранее!
        #define TWI_SETUP "lcd1602.h"                     // фиксим, что I2C инициализируется этой библиотекой
        #endif

        if( _rows>1 )          { lcdModes |= LCD_2LINE; } // else 1 line

        // @see datasheet: power on sequence
        {
            lcdPrepare(0x30, 0);          // HD44780: RS=0 запись в регистр команд дисплея.

            delay(LCD_WAIT_BOOT0);        // powerOn it needs wait from 15 upto 50msec
            lcdSend(3);                   // 1x3 отправка: по включению режим 8-бит - вторая тетрада не нужна!
            delay(LCD_WAIT_BOOT1);        // ждем >4.1ms
            lcdSend(3);                   // вторая отправка согласно даташит 8-бит..
            delayMicro16(LCD_WAIT_BOOT2); // ждем >100us
            lcdSend(3);                   // третья отправка 8-бит..

            lcdPrepare(0x20, 0);          // и только теперь переводим в режим 4-бита
            lcdSend(3);                   // и только теперь режим 4-бита и отправляем тетрады попарно!
        }
        lcdInit();
        delay(500);                     // чтобы было заметно глазом.. один раз.
    }

#ifdef __cplusplus
  } // extern "C"
#endif

#endif // _LCD1602_H_

// ========================================= test_lcd1602.ino =============================== //
/**
 * Пример скетча вывода на LCD1602 или аналогичные дисплеи, подключаемые по шине I2C.
 *
 * В стадии развития и тестирования ..
 *
 * Лицензия:
 * 1. Полностью свободное и бесплатное программное обеспечение. В том числе и от претензий.
 * 2. Вы вправе использовать его на свои нужды произвольным образом и на свой риск.
 * 3. Вы не вправе удалять из него строку с тегом @author или изменять её.
 * 4. Изменяя этот файл, Вы вправе дописать свои авторские данные и/или пояснения.
 *
 * Если Вам это оказалось полезным, то Вы можете по-достоинству оценить мой труд
 * "на свое усмотрение" (напр. кинуть денег на телефон "сколько не жалко")
 * @author Arhat109 <a href="mailto:arhat109@mail.ru">arhat109@mail.ru</a>, +7-(951)-388-2793
 */
//#include "arhat.h"

//#define TWI_ON    15    // Debug: Включить все режимы обработчика I2C
//#include "arhat_twi.h"  // Debug: Подключить явно тут с заданным комплектом режимов

//#define LCD_I2C_SPEED 100000 // Установить типовую скорость (внутри задано 800кгц!)
//#include "lcd1602.h"

// ************* Example *********** //

void setup() {
    lcdSetup(0x27, 16, 2, 1);
    lcdWrite("Hello, Arhat!", 13);
    lcdSetCursor(0,1);
    lcdWrite("Bye, Bye..", 10);
}
void loop()  {}

В тестовом скетче конечно же закомментировал оба #include.

Результат компиляции на моей меге:

"Скетч использует 2502 байт (0%) памяти устройства. Всего доступно 258048 байт.
Глобальные переменные используют 53 байт (0%) динамической памяти, оставляя 32203 байт для локальных переменных. Максимум: 32256 байт."

Фотки, уж как получилось:

экран:

Полез закачивать 1.8.7 .. думаю ничего особенно не изменится.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В общем, можно смеяться, но загрузка версии 1.8.7 сегодня не задалась. Три попытки скачать и все три - обрыв соединения в самом конце.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:

В общем, недождался, проверил сам - в версии 1.8.5 - "полет нормальный":

Вообще-то, компилятор честно предупредил о проблемах в строках 180-183

volatile void (* twiHookRestart)(void) = 0;   // указатель на функцию перезапуска мастера без освобождения шины (TWI_SEND_STOP)
volatile void (* twiMasterReader)(void) = 0;  // указатель на функцию "Master принял данные, куда их?"
volatile void (* twiSlaveReader)(void) = 0;   // указатель на функцию "Slave принял данные, куда их?"
volatile void (* twiSlaveWriter)(void) = 0;  // указатель на функцию "Slave всё отправил, что дальше?"

а именно он сказал, что не понимает для чего там volatile и просто выбрасывает это слово.

Sample.ino:180:41: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
Sample.ino:181:42: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
Sample.ino:182:41: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
Sample.ino:183:41: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]

Но, великим прогерам, очевидно, пофиг на предупреждение компилятора, которого они не понимают. Как не понимают и того, что указатель на самом деле не волатильный, а значит, в случае изменения указателя в процессе работы (что явно предполагалось как фича), всё может слететь к чертям и полёт перестанет быть нормальным.

Ты тут жаловался и страдал от того, что никогда не видел моего кода – на, смотри и учись. Профессионалы пишут такие вещи вот так:

void (* volatile twiHookRestart)(void) = 0; 
void (* volatile twiMasterReader)(void) = 0; 
void (* volatile twiSlaveReader)(void) = 0; 
void (* volatile twiSlaveWriter)(void) = 0; 

Тогда компилятор всё понимает, и указатель становится волатильным.

Надеюсь, на слова благодарности.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В данном месте компилятор поступил совершенно верно. Это глобалы для других режимов работы драйвера, которые тут НЕ используются. Некоторым просто "влом" разобраться с кодом, и тупо "покажи им палец" они оборжуться до икоты.

Как оно там планировалось (эти ветки драйвера - не тестировал вовсе за ненадобностью лично мне):

То, что там "возвращаемое значение функции по указателю" указано как volatile, а не volatile указатель на функцию - написано ВЕРНО. Функция, когда надо, пишется обыкновенным кодом, и настраивается как раз вот тут - указанием имени после знака присваивания этому указателю. Думаю, можно конечно и в ином месте, но планировалось так.

Надобности в "волатиль" указателе - в коде нет, а вот необходимость указать что выдаваемый функцией результат волатилен, ибо функция будет вызываться из под прерывания - мне показалось важным.

Зачем и что ты пытался править - мне осталось непонятным. Кода ты не смотрел (или не способен) - явно. А то - что показал - "несущественно". Это так, "вместо" спасибо. Дабы было понятней. :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Вообще-то написанное слово volatile, игнорируется. Значит, либо его не нужно было писать, либо писать так, чтобы не игнорировалось, а работало. А потому, всё сказанное - просто отмазка, - "гранаты не той системы".

А теперь, болле интересное

Arhat109-2 пишет:
Кода ты не смотрел

Ошибаешься, родной, смотрел и очень внимательно. И обнаружил там гораздо более серьёзные ляпы, коих там хватает. У меня даже есть проверочный код, в котором звёзды сходятся так, что вместо ожидаемого "Hello, Arhat!" выводится нечто непотребное.

Сначала я хотел его показать, но потом решил подождать реакции на первую ошибку. Всё-таки теплилась надежда, что меня поблагодарят за то, что показал её. Но вместо благодарности, я получил хамство (что в целом, характерно для данного автора) и заявление. что она "несущественна".

Ну, а показывать человеку его ошибки и объяснять откуда они берутся, когда он в ответ

Arhat109-2 пишет:

Это так, "вместо" спасибо. 

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

Цитата:
Это так. Дабы было понятней. :)

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ворота пишет:

Сначала я хотел его показать, но потом решил подождать реакции на первую ошибку. Всё-таки теплилась надежда, что меня поблагодарят за то, что показал её. Но вместо благодарности, я получил хамство (что в целом, характерно для данного автора) и заявление. что она "несущественна".

Ворота! Бесполезно.

Это же сам Великий(!), который принес в мир автоматное програмирование, захват таймера и что-то-там еще! Как ты посмел, смерд, усомниться в величии его кода? Великие не ошибаются - они предвосхищают будущее!

(когда Андриано заинтересовался скоростью отрисовки и2с экрана, у меня с Архатом уже был "великий срач"...  я уже много правил его говнокод и работу с lcd, а именно что было лишним в самой библиотеке, которую он переписал, назвав "своей" ;). Полное непонимание почему, при включенной -lto, компилятор выбрасывает его имена не помеченные "used" и т.д. НО!!! Тут нужно быть справедливым - в части мастер И2С, в неблокирующем варианте, - целиком его (Архата) код, и действительно быстрый.) Ссылку на эту поучительную дискуссию (мегасрач) я что-то не могу найти. Если попадется - кину ссылку.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Да, здесь-то дело даже не в скорости. Здесь ошибки детские, типа динамический буфер используется после освобождения и т.п. Как раз ошибки из тех, что "на простых примерах полёт нормальный". Когда человек не умеет программировать - это не беда. Тут на форуме немало ребят, которые за мою бытность здесь заметно прогрессировали. Беда, когда при этом у него космическое ЧСВ и "ксива на общагу в Мельбурне" - это уже полный аут, тут прогресса ждать не приходится. 

Вот, кстати, Влад, о том заявлении про Мельбурн. Как ты думаешь, каким должен быть возраст автора, чтобы он мог предположить, что взрослые, знающие как оформляются приглашения, люди в этот бред поверят? Может он просто школьник, а нам тут мозги пудрит? Ителлект-то точно на уровне школьника.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Скока буков, цельный трактат, код он смотрел .. ляпы нашел .. и говнокод-то Дракула правил (только забыл, цитирую по памяти: "код твой очень даже хорош" как раз в той теме .. отмотать что-ли) ..

А допереть что volatile void как "возвращаемое значение" есть .. нонсенс и банальный троллинг "гураков" (заменить буковку на свое усмотрение) - .. не доперло!

Вы сделали мой выходной. Вот за это - искреннее спасибо. Давно так не ржал.

Успокойтесь ужо .. конечно же volatile там избыточен и не нужен. И точно также там нет "динамически освобождаемых буферов" .. их там ВООБЩЕ НИКАКИХ нет, ибо они - ВНЕШНИЕ и оперделяются .. пользователем (что и есть одно из достоинств - сколько надо, столько и выдели .. сам).

:) :) фейспалм. тройной. :) :) .. как дети, чесслово..

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

И для полноты картины: "там есть серьезные ляпы, коих там хватает" - ЕСТЬ НАГЛОЕ ВРАНЬЕ. Это чтобы всем было понятно. Там код самого драйвера даже на асме лучше - практически не написать. Это лучшее, что можно создать как "драйвер для I2C"

Кто не верит - может пгобовать. Получится или тоже самое или ХУЖЕ. :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Архат,

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

В общем, я тебе говорил, что у меня есть работающий пример, который показывает проблемы. И даже говорил, что готов его показать. Но, тебе он не нужен, ты предпочитаешь упиваться величием говнокода, который

Arhat109-2 пишет:

лучше - практически не написать.

Упивайся дальше!

И не забудь переслать этот говнокод в Мельбурн, тебе дадут новую ксиву - на этот раз на пятизвёздочную общагу, брехуёк ты наш.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ворота пишет:

Вот, кстати, Влад, о том заявлении про Мельбурн. Как ты думаешь, каким должен быть возраст автора, чтобы он мог предположить, что взрослые, знающие как оформляются приглашения, люди в этот бред поверят? Может он просто школьник, а нам тут мозги пудрит? Ителлект-то точно на уровне школьника.

Нет, это тип личности такой. У меня папа еще жив, ему 78 лет. Он почти такую же пургу несет. Кстати, и про Австралию тоже... только ему "форма-2" помешала ;) ;) ;)!!!

Он в бОльшую часть своего бреда - верит. Что "вне концепции" - мозг игнорирует. Конечно, это психопатия, если строго, но для окружающих не сильно опасно... скорее забавно, если бы он Самоделкина с форума не выжил!

Если серьезно - это что-то из детства, я - не психолог, что именно судить не берусь, но проявляется как неспособность признать собственный косяк. Никогда, ни при каких обстоятельствах! Закомплексованность... Может били однокласники? ;) ;) ;)

В том сраче Архат возмущался не собой, а компилятором, который без __attribute__((used)) выкидывал имя, уж не помню какое. Мотивировал тем, что в ранних версиях ИДЕ у него "все компилировалось"... ну да, в ранних "-lto", не было. Его вывод: "-lto" страшный вред и заговор! (шучу, но сквозь слезы).

Показываю, как можно отправку в LCD ускорить, пишет, что на его экране "висло".

Недавние примеры с пресловутыми "скобками" в макросе (всем уже надоело!). Его ответ: "мой код, я сам знаю, как и где применять, скобки не нужны". Потом скобки появились, а часть ЕГО кода была расчитана на макрос БЕЗ скобок и, что очевидно, перекосилась - опять виноват не он, а "он же писал, что скобки не нужны!".

Еще в том сраче - пишу, что может ядро  сделать под его Arhat.h и его подключать и ИДЕ отдельно? Архат отбрехался тем, что ему "не интересно"... годом позже, наконец-то, он разобрался в части конфигов ИДЕ... даже на форуме какую-то банальщину писал про свою позорную самоделку. Но до перенастройки среды для использования его Arhat.h - руки, вероятно, никогда не дойдут.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Ой! У Архата проснулась самокритика! А я пропустил!

Arhat109-2 пишет:

А допереть что volatile void как "возвращаемое значение" есть .. нонсенс и банальный троллинг "гураков" (заменить буковку на свое усмотрение) - .. не доперло!

ну, наконец-то до тебя дошло, что этот говнокод писал "гурак" (заменить буковку на свое усмотрение).

Я рад, что в тебе самокритика проснулась!

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

http://arduino.ru/forum/apparatnye-voprosy/medlennaya-rabota-liquidcrystali2c это "та самая тема". И таки да. Дисплей я менял, после чего второй отрабатывал совсем иные задержки. Там писалось, пересмотрю, потерли или осталось.

Интересно, кого это тут ты назвал "самоделкиным"? Не того ли, кто САМ ничего и не делал, а только "забывал" указывать во вконтактниках (и не только) кто и что сделал в итого? Если ты решил за Клапу - то огорчу. Мое имхо - его таки "забрили" в ВСУ или Александр забанил окончательно по айпи. Самому жалко что его больше нет.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ворота пишет:

Ой! У Архата проснулась самокритика! А я пропустил!

Arhat109-2 пишет:

А допереть что volatile void как "возвращаемое значение" есть .. нонсенс и банальный троллинг "гураков" (заменить буковку на свое усмотрение) - .. не доперло!

ну, наконец-то до тебя дошло, что этот говнокод писал "гурак" (заменить буковку на свое усмотрение).

Я рад, что в тебе самокритика проснулась!

Вот опять из тебя гумно полезло .. сказать по делу - нечего, так хоть так.

В "прошлой жизни", моя кличка среди коллег - last byte, может слышал. Вирус stone (найди забавная и хорошо писанная вещица) мною ужат до 128 байт в то время без потери функциональности. Не перживай, "в жизнь" не выпущен. Я на нем хороший коньяк выиграл. На спор ужимали вчетвером.

У тебя есть шанс "переплюнуть" - сделай свой драйвер с такой же или большей функциональностью и МЕНЬШЕ размером .. зауважаю, реально. :)

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

wdrakula пишет:
Недавние примеры с пресловутыми "скобками" в макросе (всем уже надоело!). Его ответ: "мой код, я сам знаю, как и где применять, скобки не нужны".

 

Последний пример с пресловутыми скобками был конкретно этот:

ADMUX = ((src)|(16+(((neg-1)&1)<<3)+(poz)&7)|(adlar));

Был только за их "избыточность" .. ты тоже считаешь, что ИХ СЛИШКОМ МНОГО и ЭТО ФАТАЛЬНО? Точно фатально и копрокодерство? Или просто "тупой наезд"?

wdrakula пишет:
Потом скобки появились, а часть ЕГО кода была расчитана на макрос БЕЗ скобок и, что очевидно, перекосилась - опять виноват не он, а "он же писал, что скобки не нужны!".

А эта часть сентенции относится к спору с ЕвгениемП про дефайн пересчета миллисекунд в тики таймера Т0 .. там да, был косяк но .. сам пересчет был НЕ НУЖЕН нигде .. но надо же "прицепиться", пгадва жеж? :) .. зато есть что запомнить! :)

wdrakula пишет:
Еще в том сраче - пишу, что может ядро  сделать под его Arhat.h и его подключать и ИДЕ отдельно? Архат отбрехался тем, что ему "не интересно"...

Давно бы сделал .. кто-то мешает? А мне - и СЕЙЧАС не интересно ..

wdrakula пишет:
годом позже, наконец-то, он разобрался в части конфигов ИДЕ... даже на форуме какую-то банальщину писал про свою позорную самоделку. Но до перенастройки среды для использования его Arhat.h - руки, вероятно, никогда не дойдут.

Позорны пока твои сентенции .. цитату разбил специально, чтобы яснее было всем видно как круто "всё смешалось в доме облонских" .. чё там насчет "что-то из психологии", ась? :)

И да, позорная она или нет - не тебе решать. Она рабочая, АЦП работает существенно лучше (проверено), имеет ВСЕ выходы мега2560, имеет рабочую память 512 килобайт и держит периферию до 5 ампер .. у тебя есть лучше, позорник? НЕТ. Вот и не звизди.

 

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Arhat109-2 пишет:

Давно бы сделал .. кто-то мешает?

Ты мне это предлагаешь???? У тебя все со здоровьем нормально? Попробуй в "Ищу исполнителя" обратиться.

Ты, все-таки, похоже, окончательно с крышей простился...

============================

по коду:

1. драйвер твой, мы же с тобой, в той теме, проверяли ТОЛЬКО в режиме мастера. В этом режиме все было очень хорошо. Не стоит хвастать впустую, пока ты не проверишь его в слейве, мультимастере, на зашумленной передаче с обрывами и т.п.

2. по интерфейсу он несовместим с TWI, то есть подходит только для кустарного применения (т.е. только автором), но не в среде Ардуино, в том числе об этом я и писал, когда предлагал тебе (в той теме) довести ядро, на основе Arhat.h, до "ума", то есть до максимально возможной совместимости с экосистемой Ардуино.

3. В ту же тему - пример с LCD - тоже исключительно для кустарщины! Драйвер не наследует класс Print, что лишает этот набор самоделок какой-либо пользы для всех, кроме автора.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Arhat109-2 пишет:

И да, позорная она или нет - не тебе решать. Она рабочая

 

Как это "не мне"???? Ты же выложил на форум, на общий суд. И ты сам, никто за язык не тянул, рассказал, что память у тебя не заводится. Сам же, (тут могу врать - не помню) вроде как нашел причину. Не хочешь критики - так не выкладывай ничего... тебя давно уже отсюда "просят" ;) Ты ж сам тут остался, вот и слушай.

Ты выложил на суд общества свои ошибки, сам. На мой взгляд - это может и круто, но глупо. И ошибка в таймингах доступа к памяти - вполне позорная и ее можно было отловить при макетировании.

Такие платы делают, если ты не знаешь, в ДВА этапа. (я не про ЛУТ, я про нормальный заказ) - на первом делают свободное размещение корпусов и возможность модификации, и потом, после тестов, делают "боевой" гербер с плотным размещением. Но ты ж - гений, тебе тестировать ничего не надо было!

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

wdrakula пишет:

Arhat109-2 пишет:

Давно бы сделал .. кто-то мешает?

Ты мне это предлагаешь???? У тебя все со здоровьем нормально? Попробуй в "Ищу исполнителя" обратиться.

Ты, все-таки, похоже, окончательно с крышей простился...

============================

по коду:

1. драйвер твой, мы же с тобой, в той теме, проверяли ТОЛЬКО в режиме мастера. В этом режиме все было очень хорошо. Не стоит хвастать впустую, пока ты не проверишь его в слейве, мультимастере, на зашумленной передаче с обрывами и т.п.

2. по интерфейсу он несовместим с TWI, то есть подходит только для кустарного применения (т.е. только автором), но не в среде Ардуино, в том числе об этом я и писал, когда предлагал тебе (в той теме) довести ядро, на основе Arhat.h, до "ума", то есть до максимально возможной совместимости с экосистемой Ардуино.

3. В ту же тему - пример с LCD - тоже исключительно для кустарщины! Драйвер не наследует класс Print, что лишает этот набор самоделок какой-либо пользы для всех, кроме автора.

1. Согласен. И в той  теме и сам тоже проверял только режим мастера. Все остальные режимы - НЕ проверялись (о чем даже тут сказано выше!). Режим мастера - что-то около 190 байт, вылизывался именно он. Дерзай, улучшай хотя бы только его .. пусть будет так. Только фигушки .. ни у тебя ни у ворот нифига не выйдет. Там даже на асме мало что выйдет.

2. В чем конкретно он "несовместим с TWI" .. снова газируешь лужу? Как же Вы врать-то горазды! Протокол I2C(TWI) там выдержан полность (и в остальных режимах тоже, просто не тестировалось .. может чего и пропустил, но скорее всего заработает без изменений). Совместимость с "экосистемой" .. не смеши мои тапки. Оно вполне способно вызываться из этой "экосистемы" и работать нормально.

3. Драйвер и НЕ ДОЛЖЕН наследовать класс и тем более кривой Print, вместе со Stream, String и прочей дури из "экосистемы". Это "экосистема" может использовать драйвер, втянув его в свои классы. Тут придется "допиливать"  - создавать оболочку (класс) под применение в Print. Не проблема - для этого есть "приватный" блок функций, базовая функция вывода заданного количества байт в интерфейс. Дело "левой-задней ноги" в общем-то. Но, и ещё раз:

мне - не интересна "совместимость" с эко системой Ардуино .. ни разу, ни в одном глазу. Ты - предлагал (зачем-то) - тебе и делать (если способен конечно). Ибо "инициатива - наказуема" и "эко-система" - явление ОТКРЫТОЕ. Кому надо - тот и делает, мне - не надо. Предлагал - ты. В "чем пгоблема"? :)

4. Насчет платы: ПОКАЖИ ЛУЧШЕ прежде чем звиздеть. Зависть - плохой советчик.

wdrakula пишет:
Как это "не мне"???? Ты же выложил на форум, на общий суд. И ты сам, никто за язык не тянул, рассказал, что память у тебя не заводится.

Уже не просто "заводится" а давно работает и сын с ней тоже .. проснись или почитай последние комменты в той теме. :)

wdrakula пишет:
Сам же, (тут могу врать - не помню) вроде как нашел причину. Не хочешь критики - так не выкладывай ничего... тебя давно уже отсюда "просят" ;) Ты ж сам тут остался, вот и слушай.

Несколько "постоянных" флудерастов форума, типа тебя? http://arduino.ru/forum/apparatnye-voprosy/problema-s-otobrazheniem-info... - последний "образчик" таких просьб .. сколько людей вы уже ТАК ВЫГНАЛИ с форума, считал? Могу помочь..

[quote=wdrakula]Ты выложил на суд общества свои ошибки, сам. На мой взгляд - это может и круто, но глупо. И ошибка в таймингах доступа к памяти - вполне позорная и ее можно было отловить при макетировании.

Такие платы делают, если ты не знаешь, в ДВА этапа. (я не про ЛУТ, я про нормальный заказ) - на первом делают свободное размещение корпусов и возможность модификации, и потом, после тестов, делают "боевой" гербер с плотным размещением. Но ты ж - гений, тебе тестировать ничего не надо было![quote]

Позорно то - что ты тут пишешь. Оценщик позорности чужих ошибок .. тоже мне "судия", блин. :)

Такие платы делают много как ПО-РАЗНОМУ, в т.ч. и так и этак. И с чего ты решил что "макетирования не было"? Только потому что не изложено и вопросов не задавалось? Задавались кстати .. только они чутка "выше" вашего уровня понимания, как показала практика. В т.ч. и твои посты, что тут, что в теме за драйвер у andriano..

А плата - получилась просто замечательная, чему рад несусветно, ибо да - "не специалист-электронщик" .. так "проба пера" .. но, сейчас, по-тихоньку, таки решается вопрос её серийного производства. Ибо - ПОЛУЧИЛОСЬ. И не тебе - бездарю, решать "позорная она или нет". Есть спецы, и их мнение мне уже известно.

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

И в дополнение, а то в магаз нужно ехать:

Не получаю удовольствие от разрушения иллюзий, но если ты, Архат, наберешь в Гугле "non blocking arduino TWI", то увидишь то, чего и следовало ожидать: много разных людей тоже решали эту задачу. Не каждый из них "придумал Битлз и объявил дефолт"(с), но драйверов TWI написано реально много.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:
Это лучшее, что можно создать как "драйвер для I2C"

Кто не верит - может пгобовать.

Попробовал.

Загружаю в Nano

//
//  здесь идёт вся твоя байда, уж не буду копировать
//
void setup() {
    lcdSetup(0x27, 16, 2, 1);
    const unsigned long uSec = micros();
    lcdWrite("ArhatSuperProger", 16);
    const unsigned long intrval = micros() - uSec;
    Serial.begin(115200);
    Serial.print(intrval);
}
void loop()  {}

Получаю время - 80300

Теперь беру самую обыкновенную LiquidCrystalI2C, загружаю

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup(void) {
	lcd.begin();
	lcd.backlight();
	const unsigned long uSec = micros();
	lcd.print("ArhatSuperProger");
	const unsigned long intrval = micros() - uSec;
	Serial.begin(115200);
	Serial.print(intrval);
}

void loop(void) {}

Получаю время 23288

Ну, а теперь расскажи мне, что блокирующий код гораздо лучше неблокирующего и что если моя программа будет 57 миллисекунд сидеть в твоём говнокоде, то это значительно лучше, чем если бы она занималась чем-то своим.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Arhat109-2 пишет:

2. В чем конкретно он "несовместим с TWI" .. снова газируешь лужу? Как же Вы врать-то горазды! Протокол I2C(TWI) там выдержан полность (и в остальных режимах тоже, просто не тестировалось .. может чего и пропустил, но скорее всего заработает без изменений). Совместимость с "экосистемой" .. не смеши мои тапки. Оно вполне способно вызываться из этой "экосистемы" и работать нормально.

Блин! Ты "дурака включил" или правда не понимаешь понятия "интерфейс"?

Все эти методы из TWI: beginTransmission(), requestFrom()  и прочее. Чтобы темплейтами можно было выбирать библиотку, НЕ ПЕРЕДЕЛЫВАЯ иной код, верхнего уровня.

Иногда меня удивляет, ты что-то про программирование, кроме своего сомнитетельного опыта в хакерстве 20 летней давности, слышал? Что-то про ООП, про С++?

Чтобы твой драйвер БЫЛ ВОСТРЕБОВАН, он должен быть совместим с экосистемой Ардуино по интерфейсам (хотя бы). Не кто-то должен применять твои функции, делая на их основе методы, а ты сам, ну если претендуешь на звание программиста и выкладываешь что-то для всех. Или не выкладывай... тебе уже сказали. Или не пиши, что это кому-то нужно и полезно, кроме тебя.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ворота пишет:

Arhat109-2 пишет:
Это лучшее, что можно создать как "драйвер для I2C"

Кто не верит - может пгобовать.

Попробовал.

Загружаю в Nano

//
//  здесь идёт вся твоя байда, уж не буду копировать
//
void setup() {
    lcdSetup(0x27, 16, 2, 1);
    const unsigned long uSec = micros();
    lcdWrite("ArhatSuperProger", 16);
    const unsigned long intrval = micros() - uSec;
    Serial.begin(115200);
    Serial.print(intrval);
}
void loop()  {}

Получаю время - 80300

Теперь беру самую обыкновенную LiquidCrystalI2C, загружаю

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup(void) {
	lcd.begin();
	lcd.backlight();
	const unsigned long uSec = micros();
	lcd.print("ArhatSuperProger");
	const unsigned long intrval = micros() - uSec;
	Serial.begin(115200);
	Serial.print(intrval);
}

void loop(void) {}

Получаю время 23288

Ну, а теперь расскажи мне, что блокирующий код гораздо лучше неблокирующего и что если моя программа будет 57 миллисекунд сидеть в твоём говнокоде, то это значительно лучше, чем если бы она занималась чем-то своим.

Я так и думал .. маладец. Там специально тебе дана ссылка на прошлое обсуждение, замеры и пр. .. знакомься.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

wdrakula пишет:

Arhat109-2 пишет:

2. В чем конкретно он "несовместим с TWI" .. снова газируешь лужу? Как же Вы врать-то горазды! Протокол I2C(TWI) там выдержан полность (и в остальных режимах тоже, просто не тестировалось .. может чего и пропустил, но скорее всего заработает без изменений). Совместимость с "экосистемой" .. не смеши мои тапки. Оно вполне способно вызываться из этой "экосистемы" и работать нормально.

Блин! Ты "дурака включил" или правда не понимаешь понятия "интерфейс"?

Все эти методы из TWI: beginTransmission(), requestFrom()  и прочее. Чтобы темплейтами можно было выбирать библиотку, НЕ ПЕРЕДЕЛЫВАЯ иной код, верхнего уровня.

Иногда меня удивляет, ты что-то про программирование, кроме своего сомнитетельного опыта в хакерстве 20 летней давности, слышал? Что-то про ООП, про С++?

Чтобы твой драйвер БЫЛ ВОСТРЕБОВАН, он должен быть совместим с экосистемой Ардуино по интерфейсам (хотя бы). Не кто-то должен применять твои функции, делая на их основе методы, а ты сам, ну если претендуешь на звание программиста и выкладываешь что-то для всех. Или не выкладывай... тебе уже сказали. Или не пиши, что это кому-то нужно и полезно, кроме тебя.

Звиздец.. не читатель? Несколько раз специально повторил "эко система" (читай: прямое включение в код библиотек Ардуино и писанных поверх неё) - МЕНЯ НЕ ИНТЕРЕСУЕТ. совместимость с TWI - это ИСКЛЮЧИТЕЛЬНО совместимость с ПРОТОКОЛОМ TWI и его аппаратурой. Ибо TWI - это НЕ ардуино-вийринг гумно.

То есть прогазировал лужу на предмет использования в виринг-поделках, но уточнить про это "забыл" .. "случайно" ли? :) Вот так вот ваше вранье и всплывает на форуме регулярно.

И ещё раз: меня мало волнует "востребованность" ардуинщиками моих поделок. Ардуино - ОТКРЫТАЯ среда. Кому надо "причесать" - тот причесывает.

И на предыдущее: да, после сделанного драйвера нашел много иных вариантов .. и чё? Что-то хуже, что-то "почти такое же" .. есть даже ассемблерные варианты и? Вы же тут пыжитесь ДОКАЗАТЬ что эта поделка - гумно! Только сами написать лучше .. а от слова НИКАК. :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Даже не знаю, где "там" и что за ссылка. Только замер я сам сделал и получил, что твоя суперлиба в 3,4 раза медленне самого простого, распространённого решения для чайников. Есть что добавить?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

http://arduino.ru/forum/apparatnye-voprosy/medlennaya-rabota-liquidcrystali2c мне не трудно повторить ссыль. Осваивай.

P.S. И ещё, Дракуле: ваще-то мой драйвер "востребован". Его пользуют и даже в разделе "Ищу Исполнителя" (делал контрольную закупку у одного перца, знаю достоверно, но "сдавать" - не стану, не ворота же!) Чел, просто написал то, про что ты тут ратуешь и спокойно юзает и "на заказ" тоже. :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Посмотрел по ссылке. Про твоё "пиздобольство" и верчение ужом на сковородке, чтобы ошибку не признать, нашёл, а вот про прямое сравнение времени (как у меня) там нет.

Цифры, что я привёл - просто с монитора порта, воспроизвести их может любой. И результат там такой, какой получился.

Я так понимаю, что ничего по этим цифрам не будет? Правильно? Код архата лучше уже потому, что это код архата - критерий лучшести такой. 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

За сим из темы выпиливаюсь, если возникнут вопросы по делу - пишите, постараюсь ответить. Отвечать флудерастам больше желания нет, повеселились и хватит. пора делами заняться. :)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ворота! Ты загляни в ту тему, там было соревнование между моим и2с "ногодрыгом" для Тиньки13 и Архатовской поделкой. После сильного "причесывания" библиотека Архата выиграла с показателем около 10 мкс на символ или даже меньше. Но там было много изменений в части LCD, про которые Архат сейчас забыл, вот ты и получил плохой результат.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Влад, я получил то, что получил. Что там было в других местах, я не знаю, но человек заявил, что его код "лучше не напишешь",я сравнил и попросил прокомментировать полученный объективный результат. Кроме хамства - никаких комментариев. Как всегда.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:

За сим из темы выпиливаюсь, если возникнут вопросы по делу - пишите, постараюсь ответить. 

Мои цифры (сравнение скорости) были А) объективны и Б) совершенно по делу.

Но ответа, как я понял, не будет.

И да, кстати,

Arhat109-2 пишет:

допереть что volatile void как "возвращаемое значение" есть .. нонсенс 

Это у прогеров нонсенс. А вот в стандарте языка,которого ты не знаешь, есть обсуждения и volatile void, и const void и даже const volatile void. Но тебя, как я понимаю, это не волнует.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Твой тест у меня показал 2000мксек на строку в 16 символов .. что я сделал не так? Эх-ты .. код он читал .. :)

Всё, не интересен больше. Аут.

mixail844
Offline
Зарегистрирован: 30.04.2012

Arhat109-2 пишет:

Твой тест* у меня показал 2000мксек на строку в 16 символов .. что я сделал не так? Эх-ты .. код он читал .. :)

Всё, не интересен больше. Аут.

при условии что * =

Ворота пишет:

Теперь беру самую обыкновенную LiquidCrystalI2C, загружаю

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup(void) {
	lcd.begin();
	lcd.backlight();
	const unsigned long uSec = micros();
	lcd.print("ArhatSuperProger");
	const unsigned long intrval = micros() - uSec;
	Serial.begin(115200);
	Serial.print(intrval);
}

void loop(void) {}

Получаю время 23288

Ну, а теперь расскажи мне, что блокирующий код гораздо лучше неблокирующего и что если моя программа будет 57 миллисекунд сидеть в твоём говнокоде, то это значительно лучше, чем если бы она занималась чем-то своим.

 

Ворота пишет:

Попробовал.

Загружаю в Nano

//
//  здесь идёт вся твоя байда, уж не буду копировать
//
void setup() {
    lcdSetup(0x27, 16, 2, 1);
    const unsigned long uSec = micros();
    lcdWrite("ArhatSuperProger", 16);
    const unsigned long intrval = micros() - uSec;
    Serial.begin(115200);
    Serial.print(intrval);
}
void loop()  {}

Получаю время - 80300

ээм ... 2000мкс тоже < 80300мкс. . или это тоже троллинг для понимающих ? 

 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:

что я сделал не так?

А х тебя знает, что ты сделал не так. Я свой код показал - любой может повторить. Ты свой скрыл. Думаю, правды в твоих словах - как в брехне про Мельбурн.

В общем, пойди постирай штанишки и тявкай на взрослых дядей. До свиданья!

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

mixail844 пишет:

Arhat109-2 пишет:

Твой тест* у меня показал 2000мксек на строку в 16 символов .. что я сделал не так? Эх-ты .. код он читал .. :)

Всё, не интересен больше. Аут.

при условии что * =

Ворота пишет:

Теперь беру самую обыкновенную LiquidCrystalI2C, загружаю

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup(void) {
	lcd.begin();
	lcd.backlight();
	const unsigned long uSec = micros();
	lcd.print("ArhatSuperProger");
	const unsigned long intrval = micros() - uSec;
	Serial.begin(115200);
	Serial.print(intrval);
}

void loop(void) {}

Получаю время 23288

Ну, а теперь расскажи мне, что блокирующий код гораздо лучше неблокирующего и что если моя программа будет 57 миллисекунд сидеть в твоём говнокоде, то это значительно лучше, чем если бы она занималась чем-то своим.

 

Ворота пишет:

Попробовал.

Загружаю в Nano

//
//  здесь идёт вся твоя байда, уж не буду копировать
//
void setup() {
    lcdSetup(0x27, 16, 2, 1);
    const unsigned long uSec = micros();
    lcdWrite("ArhatSuperProger", 16);
    const unsigned long intrval = micros() - uSec;
    Serial.begin(115200);
    Serial.print(intrval);
}
void loop()  {}

Получаю время - 80300

ээм ... 2000мкс тоже < 80300мкс. . или это тоже троллинг для понимающих ? 

 

Конечно меньше и это тоже да - троллинг. В части "я смотрел твой код" .. нифига он НЕ смотрел, а если смотрел - то ничего не понял. :)

Ладно, раз уж тут есть ещё читатели, которые могут быть и просто Ардуинщиками (не только преподами, сдающими студентов ректорам), поясню (все равно Вы - не в теме, да и ТС-у похоже уже "все равно"):

Основных идей этой реализации несколько.

1. Минимизировать код драйвера прерывания I2C, в т.ч. устранив на этапе компиляции код "ненужных частей". В частоности, если речь только за вывод на дисплей (как тут), то незачем компилировать и вставлять куски из режимов "slave" и др. Реализовано набором #define констант компиляции;

2. Разрешить устанавливать ЛЮБУЮ доступную скорость драйверу для "мег". Добавлена спец. функция расчитывающая требуемые настройки twiSpeed() .. доступны скорости от единиц герц до 880Мгц включительно (см. комменты). Низкая скорость позволяет отлаживать интерфейс ваще примитивными методами, вплоть до "светодиодами". I2C такое в общем-то позволяет;

3. Не потерять функциональность типового I2C. Кроме режима masterTX остальные в общем-то не тестировались, но делались с тщательной оглядкой на даташит и известные на тот момент реализации. Вроде "должно взлететь сразу". Просто проверять было (да и сейчас) не на чем. В общем-то есть всё, в т.ч. и "мультимастер";

4. Уйти от "фиксированного" буфера приема/передачи. Здесь все буфера задаются извне - из программы, использующей интерфейс. Сколько надо (как в блоке lcd) столько и заказали. Мелкая экономия памяти;

Реализация работы с LCD-экраном делалась из-за того, что ВСЕ имеющиеся у Ардуино библиотек - кривы "безбожно", как раз потому .. что используют традиционный пакет классов, ещё от Stream. Желание "объять необъятное" приводит просто к чудовищной потере производительности интерфейсов. А для LCD ещё и включен Wire, который ни разу не учитывает "особенности" этих дисплеев.

Недостаток этих дисплеев (точнее переходников на базе PCF8574 и, как следствие режима "4-бита") в том, что они никак не сигнализируют в Ардуино что "запись завершена", но при этом работают (с т.з. микроконтроллера) офигительно медленно. По даташиту цикл записи в дисплей "от" 37 микосекунд, а в реальности больше 41 и мне попался экземпляр .. около 5 миллисекунд. Тот, что есть сейчас устойчиво рисует только с 50 микросекундной задержкой. Предыдущий держал 41.

То есть, для отправки каждого полубайтика на дисплей надо сделать 3 передачи по шине I2C: установить строб записи, выдать "значение" и снять строб записи. И так 2 раза на символ. Типовые реализации "стека Ардуино" силами wire.h КАЖДЫЙ РАЗ каждую из 6-и отправок сопровождают: а) инициализацией шины, отправкой адреса, отправкой данных, ожиданием. Итого имеем 3*6 = 18 действий на каждый рисуемый символ. Плюс "ожидания" (дисплей - молчит как партизан, принял он или ещё нет).. вот этот "гемморой" и устранен тут.

Что смотрел-смотрел, да не высмотрел "наш Херой"? :)

В блоке из lcd есть функция lcdSend() которая отдает заданное количество байт ОДНОЙ посылкой TWI. Именно она и отправляет одной посылкой все требуемые 6 байт в дисплей на каждый символ (2 полубайта по три отправки). Делает она это ОДНОЙ настройкой шины и отправляя адрес устройства тоже только 1 раз.

Так вот в ней и стоит "задержка" для компенсации партизанства дисплея .. банальный delay(). И стоит он там в МИЛЛИсекундах. И константа там указана в 5 мсек. Собственно это единственное место, требующее "донастройки" пользователем. Надо заменить на задержку в МИКРОСЕКУНДАХ и проверить тянет ли дисплей даташитовские 37 микосекунд. Всё.

В коде оставлено так - СПЕЦИАЛЬНО, чтобы они высрали тут всю свою безмозглость. Как раз для тестирования таких вот "преподов".

(кому надо было из ардуинщиков, до сих пор писал приватно на мыло)

В общем - пользуйтесь. Это реально лучшее что есть в Сети. Или одно из.  Уж точно не ЛиквидКристалл. :)

inspiritus
Онлайн
Зарегистрирован: 17.12.2012

Нууу тс-у совсем не все равно . Просто тс тихо фигеет от нечаянно спровоцированного срача:)

Arhat я попробовал твое предложение. Заработало. Спасибо.

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

Кстати выпилил с ноутбука  который был в другом городе 1.8.3 с рабочей библиотекой . 

Пока работают оба варианта. Буду дальше тестить. 

Еще раз спасибо.

Logik
Offline
Зарегистрирован: 05.08.2014

А чего тихо фигеть, надо активно участвоват в дискусии ;)

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

Arhat109-2 пишет:

2. Разрешить устанавливать ЛЮБУЮ доступную скорость драйверу для "мег". Добавлена спец. функция расчитывающая требуемые настройки twiSpeed() .. доступны скорости от единиц герц до 880Мгц включительно 

Архат, ты ошибся или опечатался ?

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:
Это реально лучшее что есть в Сети.
При этом ты упорно отказываешься посмотреть уже трижды предлагаемый пример, на котором твой говнокодпросто валится и показывает грязь

А отмазки про настройик не принимаются. Тот код, что выложен в этом топике ВТРОЕ медленнее ликвид кристала. А то, что у тебя он показал сильно меньше - брехня. Или, код был другой. Один и тот же код так не делает. 

Ну и облажамс с волатайл никто не отменял. Я, конечно,понимаю, что ты его вставил исключительно. чтобы меня потроллить, только брехня это. А обосрамс - совершенно объективен - не омажешься. Так-то, брехуёк наш обкакавшийся.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

xDriver пишет:

Архат, ты ошибся или опечатался ?

Это у него суперлиба такая. Щас чуток настройки подкрутит - до гигагерца дойдёт.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

inspiritus пишет:
Нууу тс-у совсем не все равно...
Мне показалось, что Вы испугались флудерастов.

Для Вас конкретно: в этом коде есть несколько интересных моментов или особенностей (кому как больше нравится) см. пост №4:

1. Установка скоростей - функция twiSetRate(), все допустимые скорости и диапазоны там перечеслены в комментариях. В разных диапазонах - разный "шаг" изменения скорости I2C. Тут только установка в регистры. Вычисление значений в регистрах и их запись  можно делать функцией twiSpeed(). Устанавливает ближайший диапазон и в нем ближайшую возможную скорость.

2. Макросы управления автоматом I2C собственно Вам, как пользователю - не нужны. На них написан автомат - обработчик прерываний, в общем-то исключительно для удобства чтения его кода и только.

3. Целевой адрес приема/передачи хранится в глобале twiSLARW при необходимости, этим можно пользоваться "ручками", но осторожно. Для мультимастерных и slave режимов, собственный адрес меги устанавливается макросом twiSetAddress(), а twiSetMaskAddress() устанавливает маску адреса (см. описание в даташите). При собственном развитии далее, макросы twiStart() и twiStop() и др. принимают в качестве параметра признак надо ли свистеть ACK в шину или "обойдется". В ряде случаев - НЕ НАДО.. но тут только пример (lcd) что завершаем передачу штатно, освобождая шину.

4. Функции (режимы кроме "мастер-передатчик") вызываются через указатели, что позволяет "динамически" менять поведение интерфейса, скажем при "перекрестном" обслуживании нескольких устройств. При их написании надо помнить, что все они вызываются из под прерывания автомата TWI и соответственно не должны делать ничего "сложного" .. заполнился буфер приема - куда его? Что тут можно "быстро" сделать? Да просто закрыть работу с этим(!) буфером и свистнуть в главный цикл "принял". Или установить признак "принял" и взять следующий буфер, если он есть. Аналогично со всеми остальными.

5. строки 171-180 arhat_twi.h - остаток (отсюда выпилил) блока статистики автомата TWI. Можно воткнуть в обработчик учет статистики принятого/переданного а также всех возможных ошибок линии и автомата.

6. twiSetup() только настраивает автомат, но не запускает его!

7. twiSendStop() -- о .. с этим местом пришлось поизголяться .. это исключительно для оптимизации обработчика прерывания TWI. Там "хитрость" в том, что если компилирован только 1 режим (всё равно какой) то лучше иметь "макрос", а если хотя бы 2 режима используются одновременно, то правильнее (меньше места) при вызове как функции. Это лучше не трогать и не пользовать. Исключительно "внутренняя оптимизация" обработчика. (именно этим он и отличается от остальных неплохих релизов)

8. Выделены "protected" методы. Исключительно для развития и встраивания "куда-нибудь". Всё необходимое для написания своих функций "верхнего уровня" тут есть.

9. "public" раздел представлен тремя базовыми функциями. Всё остальное дополняется по аналогии, проблем нет.

Файл arhat_lcd.h (второй):

1. В комментах тайминги из даташита "как есть". Работают, но с особенностями. В частности I2C версии не используют бит готовности дисплея из-за чего невозможно узнать, что дисплей обработал принятый символ. Только подбором тайминга, о чем писал выше через задержку в lcdSend(). Они мне уже всречались разные.

2. Этот хидер, инклудит в себя предыдущий (twi) и при вызове twiSetup() "метит" в его константе TWI_SETUP "себя", что позволяет в дальнейшем "знать" в программе "кто" первый активировал интерфейс (по результату компиляции!) при наличии нескольких "работников" с ним. Может оказаться полезным, мне такое решение показалось интересным и теперь применяю его везде в своих разработках. строки 193-195. То есть, эта константа будет иметь значение того "работника", который первым был скомпилирован компилятором. Мелочь, но приятно. :)

3. В этом файле скорость работы I2C установлена на 800кГц. Это предел, который тянет интерфейс PCF8574 и собственно приемный буфер дисплея. То, что оно ПОТОМ обрабатывает символ долго .. иная проблема. Но, закидать 6-и байтовую посылку в дисплей вполне можно на этой скорости. Если не тянет - можно понизить, там есть константа.

4. lcdSetup() в своем конце исполняет паузу в 0.5сек. Это специально для "увидеть" глазками инициализацию дисплея. Можно убрать, если критично. Для штатной очистки экрана есть lcdInit().

5. lcdSetCursor() опирается на "знание" длины строки дисплея в 40 символов. Если это не так - надо править тут. Опять же, если строк больше (тут только 2) - то сюда же с правкой.

6. lcdSend() -- бутылочное горлышко через которое идет весь обмен с дисплеем. Вот как раз тут и стоит "та самая" задержка для преодоления "партизанства" контроллера I2C дисплеев. Сам протокол I2C и его автомат - корректны. Они будут нормально ждать признака готовности .. но передача пачки .. увы не имеет способа синхронизации с контроллером самого дисплея.

Вариантов тут несколько:

а) устанавливать минимально возможную задержку по своему прибору. У меня один терпел 41мксек, а вот второй только 50мксек. Но .. и был очень большой тормоз .. :)

б) устанавливать большую(!) задержку и переключать исполнение кода на что-то ещё. Использованный публично delay() - как пример. Он имеет внутри себя функцию yeld(), в которой можно назначать некое исполнение фоном. Вывод на дисплей имеющий всего 16 символов в строке (да даже и все 40 - скроллирование) не имеет смысла быстрей 5мсек на символ. Вот для этого там и стоит такой delay() - подключаем что нужно в yeld() и не паримся. :)

в) реорганизация кода этой функции под RTOS и/или "конченные автоматы" так, чтобы она работала не блокирующе, если нет передачи или не завершена отправка предыдущей пачки. Тут можно поизголяться на предмет "динамического приоритета исполнения" - величины паузы.

.. остальное там сильно примитивно и не вижу смысла расписывать как-то. Как пример, содержащий много полезных трюков, а также дающий минимальный код как по программной так и по оперативной памяти и имеющий максимально возможную скорость работы .. считаю очень полезным для пополнения опыта. (моего конечно, в первую очередь)

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ворота пишет:

Arhat109-2 пишет:
Это реально лучшее что есть в Сети.
При этом ты упорно отказываешься посмотреть уже трижды предлагаемый пример, на котором твой говнокодпросто валится и показывает грязь

А отмазки про настройик не принимаются. Тот код, что выложен в этом топике ВТРОЕ медленнее ликвид кристала. А то, что у тебя он показал сильно меньше - брехня. Или, код был другой. Один и тот же код так не делает. 

Ну и облажамс с волатайл никто не отменял. Я, конечно,понимаю, что ты его вставил исключительно. чтобы меня потроллить, только брехня это. А обосрамс - совершенно объективен - не омажешься. Так-то, брехуёк наш обкакавшийся.

Как писал Дракула (перефразирую):

Тут осталось только вспомнить терминологию своего дружка из "ветеринарного техникума" .. :) Кто и КАК тут обделался, в общем-то видно и ясно уже всем, а не только лишь каждому.