Перейти к содержанию

Не могу совладать с uint8_t при работе с LCD1602A


Рекомендуемые сообщения

Ребята всем привет.

Нужна Ваша помощь.

Ситуация такая.

Делаю устройство для считывания байтов с микрухи 24LC64. 

На базе ATMEGA16 и китайского индикатора LCD1602A. Программу пишу в ATMEL STUDIO 6.

Запись байтов  происходит нормально. А вот считывание не очень. На индикаторе постоянно высвечивается какой-то мусор. 

Уже три дня прыгаю с бубном и результата ноль.

Может Вы дадите совет.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "i2c_eeprom.h"
#include <string.h>
#include <avr/interrupt.h>

#define e1 PORTD|=0b00001000 // установка линии E в 1
#define e0 PORTD&=0b11110111 // установка линии E в 0
#define rs1 PORTD|=0b00000100 // установка линии RS в 1 (данные)
#define rs0 PORTD&=0b11111011 // установка линии RS в 0 (команда)

void eeInit(void)
{
    /*Настраиваем Генератор скорости связи*/
    TWBR = (F_CPU/slaveF_SCL - 16)/(2 * /* TWI_Prescaler= 4^TWPS */1);
    
/*
Если TWI работает в ведущем режиме, то значение TWBR должно быть не менее 10. Если значение TWBR меньше 10, то ведущее устройство шины может генерировать некорректные сигналы на линиях SDA и SCL во время передачи байта.
*/
    if(TWBR < 10)
        TWBR = 15;

    /*
Настройка предделителя в регистре статуса Блока управления.
Очищаются биты TWPS0 и TWPS1 регистра статуса, устанавливая тем самым, значение предделителя = 1.
    */
    TWSR &= (~((1<<TWPS1)|(1<<TWPS0)));
}
uint8_t eeWriteByte(uint16_t address,uint8_t data)
{

/*****УСТАНАВЛИВАЕМ СВЯЗЬ С ВЕДОМЫМ********/

    do
    {
//Инициализация Регистра управления шиной в Блоке управления
/*Перед началом передачи данных необходимо сформировать т.н. условие начала. В состоянии покоя линии SCL и SDA находятся на высоком уровне. Ведущее устройство (Контроллер AVR в нашем примере), которое хочет начать передачу данных, изменяет состояние линии SDA к низкому уровню. Это и есть условие начала передачи данных.*/
        
/*
а)Сброс флага прерывания TWINT (Флаг TWINT сбрасывается программно путем записи в него логической 1) для разрешения начала новой передачи данных 
б)Уст. бит условия СТАРТ
в)Уст. бит разрешения работы TWI
*/
        TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

//Ждем, пока шина даст добро (возможно, линия пока еще занята, ждем)
//TWINT бит устанавливается аппаратно, если TWI завершает текущее задание и ожидает реакции программы
        while(!(TWCR & (1<<TWINT)));

        /*Проверяем регистр статуса, а точнее биты TWS3-7,
        которые доступны только для чтения. Эти пять битов 
        отражают состояние шины. TWS2-0 "отсекаем" с помощью операции "И     0xF8". Если TWS7-3 = 0x08, то СТАРТ был успешным.*/
        if((TWSR & 0xF8) != TW_START)
            return false;

/*К шине I2C может быть подключено множество подчиненных устройств (к примеру, много микросхем внешней памяти EEPROM). Для того, чтобы все микросхемы и контроллер знали, от кого и кому передается информация, в протоколе реализована Адресация ведомых устройств. В каждой микросхеме, предназначенной для работы с I2C, на заводе "зашит" определенный адрес. Мы этот адрес передаем по всей шине, т.е. всем ведомым. Каждый ведомый получает этот адрес и смотрит, типа мой это или чужой. Если мой, то О КРУТО, со мной хочет работать контроллер AVR. Так вот и происходит "рукопожатие" между ведущим и ведомым.*/

/*Так вот, мы хотим работать с микросхемой памяти 24LC64, поэтому по шине нам надо передать ее адрес. Она узнает свой адрес, и будет знать, что данные на запись адресуются именно ей. А остальные микросхемы, если они есть, эти данные будут просто игнорировать.*/

/*Постоянная часть адреса 24LC64 - 1010 (см. даташит на 24XX64), 3 бита - переменные (если вдруг мы захотим подключить несколько одинаковых микросхем c одинаковыми заводскими адресами, они пригодятся; в ином(нашем) случае выставляем нули), далее бит 0 - если хотим записывать в память или 1 - если читаем данные из памяти I2C EEPROM*/
        
    //TWDR = 0b1010'000'0;    
        TWDR = (slaveAddressConst<<4) + (slaveAddressVar<<1) + (WRITEFLAG);

/*Говорим регистру управления, что мы хотим передать данные, содержащиеся в регистре данных TWDR*/
        TWCR=(1<<TWINT)|(1<<TWEN);

        //Ждем окончания передачи данных
        while(!(TWCR & (1<<TWINT)));
    
/*Если нет подтверждения от ведомого, делаем все по-новой (либо неполадки с линией, либо ведомого с таким адресом нет).
Если же подтверждение поступило, то регистр статуса установит биты в 0x18=TW_MT_SLA_ACK (в случае записи) или 0x40=TW_MR_SLA_ACK (в случае чтения).
Грубо говоря, если TW_MT_SLA_ACK, то ведомый "говорит" нам, что его адрес как раз 1010'000 и он готов для записи (чтения, если TW_MR_SLA_ACK).*/
    }while((TWSR & 0xF8) != TW_MT_SLA_ACK);
        
/*Здесь можем уже уверенно говорить, что ведущий и ведомый друг друга видят и понимают. Вначале скажем нашей микросхеме памяти, по какому адресу мы хотим записать байт данных*/
    

/*****ПЕРЕДАЕМ АДРЕС ЗАПИСИ********/
    
/*Записываем в регистр данных старший разряд адреса (адрес 16-битный, uint16_t))..*/
    TWDR=(address>>8);

    //..и передаем его
    TWCR=(1<<TWINT)|(1<<TWEN);

    //ждем окончания передачи
    while(!(TWCR & (1<<TWINT)));

/*Проверяем регистр статуса, принял ли ведомый данные. Если ведомый данные принял, то он передает "Подтверждение", устанавливая SDA в низкий уровень. Блок управления, в свою очередь, принимает подтверждение, и записывает в регистр статуса 0x28= TW_MT_DATA_ACK. В противном случае 0x30= TW_MT_DATA_NACK */
    if((TWSR & 0xF8) != TW_MT_DATA_ACK)
        return false;

    //Далее тоже самое для младшего разряда адреса
    TWDR=(address);
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));

    if((TWSR & 0xF8) != TW_MT_DATA_ACK)
        return false;


/*****ЗАПИСЫВАЕМ БАЙТ ДАННЫХ********/

    //Аналогично, как и передавали адрес, передаем байт данных
    TWDR=(data);
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));

    if((TWSR & 0xF8) != TW_MT_DATA_ACK)
        return false;

    /*Устанавливаем условие завершения передачи данных (СТОП)
    (Устанавливаем бит условия СТОП)*/
    TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
    
    //Ждем установки условия СТОП
    while(TWCR & (1<<TWSTO));

    return true;
}
uint8_t eeReadByte(uint16_t address)
{
    //uint8_t data; //Переменная, в которую запишем прочитанный байт
    uint8_t data; // 1

    //Точно такой же кусок кода, как и в eeWriteByte...
    /*****УСТАНАВЛИВАЕМ СВЯЗЬ С ВЕДОМЫМ********/
    do
    {
        TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
        while(!(TWCR & (1<<TWINT)));

        if((TWSR & 0xF8) != TW_START)
        return false;

        TWDR = (slaveAddressConst<<4) + (slaveAddressVar<<1) + WRITEFLAG;
        TWCR=(1<<TWINT)|(1<<TWEN);

        while(!(TWCR & (1<<TWINT)));
        
    }while((TWSR & 0xF8) != TW_MT_SLA_ACK);
    

    /*****ПЕРЕДАЕМ АДРЕС ЧТЕНИЯ********/
    TWDR=(address>>8);   //////  
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));

    if((TWSR & 0xF8) != TW_MT_DATA_ACK)
    return false;

    TWDR=(address);
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));

    if((TWSR & 0xF8) != TW_MT_DATA_ACK)
    return false;


    /*****ПЕРЕХОД В РЕЖИМ ЧТЕНИЯ********/
    /*Необходимо опять "связаться" с ведомым, т.к. ранее мы отсылали адресный пакет (slaveAddressConst<<4) + (slaveAddressVar<<1) + WRITEFLAG, чтобы записать адрес чтения байта данных. А теперь нужно перейти в режим чтения (мы же хотим прочитать байт данных), для этого отсылаем новый пакет (slaveAddressConst<<4) + (slaveAddressVar<<1) + READFLAG.*/
    
    //Повтор условия начала передачи
    TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    //ждем выполнения текущей операции
    while(!(TWCR & (1<<TWINT)));

    /*Проверяем статус. Условие повтора начала передачи (0x10=TW_REP_START) должно подтвердиться*/
    if((TWSR & 0xF8) != TW_REP_START)
    return false;

    /*Записываем адрес ведомого (7 битов) и в конце бит чтения (1)*/
    //TWDR=0b1010'000'1;
    TWDR = (slaveAddressConst<<4) + (slaveAddressVar<<1) + READFLAG;

    //Отправляем..
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));

    /*Проверяем, нашелся ли ведомый с адресом 1010'000 и готов ли он работать на чтение*/
    if((TWSR & 0xF8) != TW_MR_SLA_ACK)
    return false;


    /*****СЧИТЫВАЕМ БАЙТ ДАННЫХ********/

    /*Начинаем прием данных с помощью очистки флага прерывания TWINT. Читаемый байт записывается в регистр TWDR.*/
    TWCR=(1<<TWINT)|(1<<TWEN);

    //Ждем окончания приема..
    while(!(TWCR & (1<<TWINT)));

    /*Проверяем статус. По протоколу, прием данных должен оканчиваться без подтверждения со стороны ведущего (TW_MR_DATA_NACK = 0x58)*/
    if((TWSR & 0xF8) != TW_MR_DATA_NACK)
    return false;

    /*Присваиваем переменной data значение, считанное в регистр данных TWDR*/
    data=TWDR;

    /*Устанавливаем условие завершения передачи данных (СТОП)*/
    TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
    
    //Ждем установки условия СТОП
    while(TWCR & (1<<TWSTO));

    //Возвращаем считанный байт
    return data;
}

//////////////////////////////////////////////////

void port_ini(void)
{
    PORTD=0x00;
    DDRD=0xff;
}
//-----------------------------------------------------------
void sendHelfByte(unsigned char per)
{
    per<<=4;
    e1;
    _delay_us(50);
    PORTD&=0b00001111;
    PORTD|=per;
    e0;
    _delay_us(50);
}
//-----------------------------------------------------------
void sendByte(unsigned char per, unsigned char mode)
{
    if(mode==0)
    {
        rs0;
    }else{
        rs1;
    }
    unsigned char hc=0;
    hc=per>>4;
    sendHelfByte(hc);
    sendHelfByte(per);
}
//-----------------------------------------------------------
void setPos(unsigned char x, unsigned char y)
{
    char adress;
    adress = (0x40*y+x)|0b10000000;
    sendByte(adress, 0);
}
//-----------------------------------------------------------
void sendChar(unsigned char per)
{
    sendByte(per, 1);
}
//-----------------------------------------------------------

void lcd_ini(void)
{
    _delay_ms(15);
    sendHelfByte(0b00000011);
    _delay_ms(4);
    sendHelfByte(0b00000011);
    _delay_us(100);
    sendHelfByte(0b00000011);
    
    _delay_us(1);
    sendHelfByte(0b00000010);
    _delay_us(1);
    
    sendByte(0b00101000, 0);
    _delay_us(1);
    
    sendByte(0b00001100, 0);
    _delay_us(1);
    
    sendByte(0b00000110, 0);
    _delay_us(1);
}
//-----------------------------------------------------------
void str_lcd(char per[])
{
    int count;
    for(count=0; per[count]!='\0'; count++)
    {
        sendChar(per[count]);
    }
}


//char simvol1;


int main(void)
{
    // Берём данные из чипа 24cl64
    //DDRA = 0xff;
    //PORTA = 0x00;
    
uint8_t byte = 0;  
uint16_t address = 6;  
char charPer;
eeInit();
    
port_ini();
lcd_ini();

setPos(0, 0);
str_lcd("Adress 0 = ");
charPer = eeReadByte(address);
setPos(11, 0);
sendChar(charPer);
setPos(12, 0);
str_lcd("    ");
setPos(0, 1);
str_lcd("                ");
}

 

 

 

//////////////////////////////////

 

i2c_eeprom.h

 

#define false 0
#define true 1

//#define slaveF_SCL 100000 //100 Khz
#define slaveF_SCL 400000 //400 Khz

#define slaveAddressConst 0b1010 //Постоянная часть адреса ведомого устройства
#define slaveAddressVar 0b000 //Переменная часть адреса ведомого устройства

//Разряды направления передачи данных
#define READFLAG 1 //Чтение
#define WRITEFLAG 0 //Запись    0

void eeInit(void); //Начальная настройка TWI
uint8_t eeWriteByte(uint16_t address,uint8_t data); //Запись байта в модуль памяти EEPROM
uint8_t eeReadByte(uint16_t address); //Чтение байта из модуля памяти EEPROM

// TWSR values (not bits)
// (taken from avr-libc twi.h - thank you Marek Michalkiewicz)
// Master
#define TW_START                    0x08
#define TW_REP_START                0x10
// Master Transmitter
#define TW_MT_SLA_ACK               0x18
#define TW_MT_SLA_NACK              0x20
#define TW_MT_DATA_ACK              0x28
#define TW_MT_DATA_NACK             0x30
#define TW_MT_ARB_LOST              0x38
// Master Receiver
#define TW_MR_ARB_LOST              0x38
#define TW_MR_SLA_ACK               0x40
#define TW_MR_SLA_NACK              0x48
#define TW_MR_DATA_ACK              0x50
#define TW_MR_DATA_NACK             0x58
// Slave Transmitter
#define TW_ST_SLA_ACK               0xA8
#define TW_ST_ARB_LOST_SLA_ACK      0xB0
#define TW_ST_DATA_ACK              0xB8
#define TW_ST_DATA_NACK             0xC0
#define TW_ST_LAST_DATA             0xC8
// Slave Receiver
#define TW_SR_SLA_ACK               0x60
#define TW_SR_ARB_LOST_SLA_ACK      0x68
#define TW_SR_GCALL_ACK             0x70
#define TW_SR_ARB_LOST_GCALL_ACK    0x78
#define TW_SR_DATA_ACK              0x80
#define TW_SR_DATA_NACK             0x88
#define TW_SR_GCALL_DATA_ACK        0x90
#define TW_SR_GCALL_DATA_NACK       0x98
#define TW_SR_STOP                  0xA0
// Misc
#define TW_NO_INFO                  0xF8
#define TW_BUS_ERROR                0x00

И вот ещё что показывает программатор что есть в памяти микрухи. То что не получается считать.....(

PoniProg51.jpg

Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

Если считывание идет правильно, то при выводе на индикатор надо считанное значение преобразовывать в символы. Использовать можно такую функцию

char BinToHex( char val )
{
  switch( val )
  {
    case 0:    return '0';
    case 1:    return '1';
    case 2:    return '2';
    case 3:    return '3';
    case 4:    return '4';
    case 5:    return '5';
    case 6:    return '6';
    case 7:    return '7';
    case 8:    return '8';
    case 9:    return '9';
    case 10:    return 'A';
    case 11:    return 'B';
    case 12:    return 'C';
    case 13:    return 'D';
    case 14:    return 'E';
    case 15:    return 'F';
    default: return ' ';
  }
}

И далее выводить по два символа

charPer = eeReadByte(address);
setPos(11, 0);
sendChar(BinToHex((charPer >> 4) & 0x0F));
sendChar(BinToHex(charPer & 0x0F));

 

Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

Большое Вам спасибо за совет и за код. Всё заработало. 

Но почему то появилась новая проблема.

При обращении к функции 

charPer = eeReadByte(address);

Я отправляю ей адрес байта для считывания. И в ответ получаю перменную uint8_t data c значением равным адресу ячейки. Вместо содержимого ячейки.

Просмотрев код ошибок не заметил. Может что то неправильно с соединением ножек 24LC64...  У меня к стати ношка 7 вообще без подключения.

Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

Присоединяйтесь к обсуждению

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

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
  • Сообщения

    • Все предложенные к рассмотрению источники питания работают примерно по одному принципу: сетевое напряжение выпрямляется, фильтруется (получаем чуть больше 300 вольт постоянного), затем преобразуется снова в переменное, но уже на частотах в несколько десятков килогерц, понижается на трансформаторе и снова выпрямляется. За счёт высокой частоты преобразования используется трансформатор на ферритовом, а не на стальном, сердечнике, гораздо меньших габаритов и стоимости. Минусы: значительное усложнение схемы блока и вероятность возникновения различных помех от него. Модули управления (кроме первого) также являются импульными преобразователями, с теми же достоинствами и недостатками. Если нужно по быстрому собрать некое подобие ЛБП, то уж лучше брать модуль вроде этого. Ну и блок питания к нему соответствующий. Но не очень понятно, какой практический опыт можно получить от соединения готовых модулей парой проводов.  
    • У меня больше всего вопросов вызвала необычная схема обеспечения отрицательного питания. Автор этой обстоятельной заметки пишет: For this supply to work correctly, the transformer must have a secondary voltage of at least 18V RMS.  Почему? Что будет не так с отрицательным питанием, если напряжение на трансформаторе будет меньше 18В?   https://tinyurl.com/23mlwxtt - я в простейшей эмуляции ставлю 12В пикового напряжения для трансформатора и на стабилитроне все как положено: -5.6В.
    • Согласен, очень криво объяснил. Это работа трёх вольтовой линии, просто на диод шотки сдвоенный, на один анод приходит сигнал напрямую с трансформатора, а на второй через дроссель. Вольт/деление 5 вольт в клетке, тайминг по моему 10 МС. Третья фотография это сигнал на катодах уровень земли ровно по центру экрана. Но все линии по итогу в порядке 3.3 в, 5, в, 12 в и -12 в. Нагрузить все линии не могу сразу ,так как тут же выгорают транзисторы (имеется нагрузка 250 ватт по 10 ампер на каждую линию за исключением-12в), поэтому нагружаю 3.3 вольтовую линию на 10 ампер,  подключаю переменный резистор 50 ватт на 15 ом на 5 вольтовую линию и постепенно довожу до той той картины с перекосом (это гдето  50 ватт общее). По поводу микросхемы, вверху имеется скрин где между импульсами проскакивает мини импульс, если так можно сказать, он проскакивает и на одной  и на второй ноге (7,8). Микросхема не tl 494, а lw4933/abx942.1/c9421646. Далее они приходят на базы транзисторов 945g  коллекторы этих транзисторов соединены с  выводами трансформатора. Просто схема типовая, легче мне кажется просто привести фото самого блока, для тех кто разбирается будет гораздо информативне.  Диод шотки по 12 вольтовой линии был подгоревший, заменил на донора. Приводить скрины не буду что бы не захламлять тему. В итоге, пока все так же, при достижении определенной нагрузки суммарно где-то 50 ватт, появляется этот "выброс и перекос". По этому имеются мысли на два варианта, это микросхема , этот мини импульс между периодами, на низкой нагрузке особо не влияет, но при достижении определенной приводит с самовозбуждению входной цепи и непроизвольному открытию транзистора нижнего плеча. Либо дело в "горячей части", плавающий дефект в обвязке силовых ключей.  Спасибо за ответ.
    • @Gomerchik а вы контролировали как меняется уровень сигнала на А1 ардуины?
    • Спасибо за совет. Автором данного проекта я не являюсь, мне нужно было воссоздать уличный датчик для метеостанции взамен пропавшего(( Из разного найденного в интернете этот проект работает с моей станцией Орегон (спасибо автору). В понедельник попробую последовать Вашему совету. Но все равно куча непоняток  как блин это работает)) Если дело в неправильной отправки команды, то как на это влияет подключение датчика температуры? Если совсем не подключать таймер, то передача идет один раз (как и прописано в программе), станция принимает и отображает, но минут через сколько-то естественно станция уже ни чего не показывает, но с таймером питание полностью не пропадает с ардуинки, но передача сигнала каким-то образом работает по таймеру.  В моем понимании данная команда подается один раз потому, что таймер должен отключать питание МК после передачи сигнала и каждые 43 сек снова подавать питание (так того требует станция).  Ардуино передает показания температуры отключается полностью и 43 секунды мк не работает.  Сейчас у меня питание пока сделано на подпитке от солнечной батареи, но пару пасмурных дней и аккумулятор съедается до отключения(
    • thickman Так и сделаю. Вытащу из бу БП.  Буду знать, как отличить. Благодарю. Заменил транзисторы на IRFB20N50K. Картина стала, совсем другой.  Похоже трудность не в драйвере, на момент подвозбуда, переходные процессы, в нем, завершены. Увеличил затворные резисторы до 50ом, стало немного лучше.  Не понятно, почему верхний ключ греется несколько сильнее. Возможно, стоит посмотреть ток в коллекторе.  Снабберные емкости временно удалил, изменений не произошло.  Замена ТГР на другой, на кольце MSTN-16A-TH, так же, результата не принесла.   irfb20n50k.pdf
×
×
  • Создать...