Ребята всем привет.
Нужна Ваша помощь.
Ситуация такая.
Делаю устройство для считывания байтов с микрухи 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
И вот ещё что показывает программатор что есть в памяти микрухи. То что не получается считать.....(