I2C шина

Модератор: boogyman

Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

I2C шина

#1

Сообщение Gimpel »

Изучаю эту шину по данному примеру: https://radioparty.ru/programming/avr/c ... son-ds1307

До этого почитал как она работает: Старт (код 0х08); Адрес, АСК, 0х18; Указатель, АСК, 0х28; Повторный старт, 0х10; Адрес+бит чтения, АСК, 0х40; либо NACK, либо сразу Стоп.

А в коде урока как-то не понятно записано:
// Функция чтения данных из DS1307
uint8_t DS1307Read(uint8_t address,uint8_t *data)
{
uint8_t res;
I2CStart(); // СТАРТ
res = I2CWriteByte(0b11010000); // адрес DS1307+W
if(!res) return 0; // ОШИБКА
// Передача адреса необходимого регистра
res = I2CWriteByte(address);
if(!res) return 0; // ОШИБКА
I2CStart(); // Повторный СТАРТ
res = I2CWriteByte(0b11010001); // адрес DS1307+R
if(!res) return 0; // ОШИБКА
// Чтение данных с "неподтверждением"
res = I2CReadByte(data,0);
if(!res) return 0; // ОШИБКА
I2CStop(); // СТОП
return 1; // OK
}
нельзя ли обойтись без res и без (uint8_t address,uint8_t *data)?
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#2

Сообщение Gimpel »

А как переделать данный пример под семисегментный индикатор?
Вложения
DS1307.zip
(15.97 КБ) 314 скачиваний
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#3

Сообщение AntonChip »

Gimpel писал(а): 10 апр 2020, 17:49 нельзя ли обойтись без res и без (uint8_t address,uint8_t *data)?
Можно и без этой переменной, она используется для выявления ошибок при обмене с DS1307
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#4

Сообщение Gimpel »

AntonChip писал(а): 10 апр 2020, 19:14
Gimpel писал(а): 10 апр 2020, 17:49 нельзя ли обойтись без res и без (uint8_t address,uint8_t *data)?
Можно и без этой переменной, она используется для выявления ошибок при обмене с DS1307
без неё не работает. она же объявлена. или надо работать с address? к которому мы прибавляем либо 0 (для записи), либо 1 (для чтения).
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#5

Сообщение AntonChip »

Вот код без возврата ошибок

Код: Выделить всё

#include <avr/io.h>
#include <util/delay.h>

// Функция инициализация шины TWI
void I2CInit(void)
{
TWBR = 2; // Настройка частоты шины
TWSR = (1 << TWPS1)|(1 << TWPS0); // Предделитель на 64
TWCR |= (1 << TWEN); // Включение модуля TWI
}

// Функция СТАРТ
void I2CStart(void)
{
TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTA); // Передача условия СТАРТ
while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
}

// Функция СТОП
void I2CStop(void)
{
TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTO); // Передача условия СТОП
while(TWCR & (1 << TWSTO)); // Ожидание завершения передачи условия СТОП
}

// Функция записи данных по шине
void I2CWriteByte(uint8_t data)
{
TWDR = data; // Загрузка данных в TWDR	
TWCR = (1 << TWEN)|(1 << TWINT); // Сброс флага TWINT для начала передачи данных
while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
}

// Функция чтения данных по шине
uint8_t I2CReadByte(uint8_t ack)
{
uint8_t data = 0;
// Возвращаем "подтверждение" после приема
if(ack) TWCR |= (1 << TWEA);
// Возвращаем "неподтверждение" после приема
// Ведомое устройство не получает больше данных
// обычно используется для распознования последнего байта
else TWCR &= ~(1 << TWEA);
// Разрешение приема данных после сброса TWINT
TWCR |= (1 << TWINT); 
while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
// Проверка статуса
// Если принят байт данных и возвращается "подтверждение"(0x50)
// или принят байт данных и возвращается "ненеподтверждение"(0x58)
if((TWSR & 0xF8) == 0x50 || (TWSR & 0xF8) == 0x58)
data = TWDR; // Читаем данные из TWDR 
return data; // OK
}

// Функция чтения данных из DS1307
uint8_t DS1307Read(uint8_t address)
{
uint8_t data = 0;
I2CStart(); // СТАРТ
I2CWriteByte(0b11010000);	// адрес DS1307+W
// Передача адреса необходимого регистра
I2CWriteByte(address);
I2CStart(); // Повторный СТАРТ	
I2CWriteByte(0b11010001);	// адрес DS1307+R
data = I2CReadByte(0);
I2CStop(); // СТОП
return data;
}

// Функция записи данных в DS1307
void DS1307Write(uint8_t address, uint8_t data)
{
I2CStart();	// СТАРТ
I2CWriteByte(0b11010000);	// адрес DS1307+W
// Передача адреса необходимого регистра
I2CWriteByte(address);
I2CWriteByte(data); // Запись данных
I2CStop(); // СТОП
}

// Функции работы с LCD 
#define RS PD0 
#define EN PD2
// Функция передачи команды
void lcd_com(unsigned char p)
{
PORTD &= ~(1 << RS); // RS = 0 (запись команд)
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}
// Функция передачи данных
void lcd_data(unsigned char p)
{
PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция вывода строки на LCD
void lcd_string(unsigned char command, char *string)
{
lcd_com(0x0C);
lcd_com(command);
while(*string != '\0')
{  lcd_data(*string);
string++;
}
}

// Функция вывода переменной
void lcd_num_to_str(unsigned int value, unsigned char nDigit)
{
 switch(nDigit)
 {
  case 4: lcd_data((value/1000)+'0');
  case 3: lcd_data(((value/100)%10)+'0');
  case 2: lcd_data(((value/10)%10)+'0');
  case 1: lcd_data((value%10)+'0');
 }
}

// Функция инициализации LCD
void lcd_init(void)
{
PORTD = 0x00;
DDRD = 0xFF;

_delay_ms(50); // Ожидание готовности ЖК-модуля

// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);

// Активизация четырехразрядного режима
PORTD |= (1 << EN);
PORTD &= ~(1 << EN);
_delay_ms(5); 

lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}

int main(void)
{  
_delay_ms(100); 

DDRC = 0x00;
PORTC = 0xFF;

lcd_init(); // Инициализация LCD
I2CInit(); // Инициализация шины I2C

lcd_string(0x81, "«acГ Ѕa DS1307"); // Часы на DS1307
lcd_string(0xC4, "  :  :  ");

// Запускаем ход часов
uint8_t temp;
temp = DS1307Read(0x00);
temp &= ~(1 << 7); // обнуляем 7 бит
DS1307Write(0x00,temp);

while(1)
{ 
uint8_t hour, minute, second, temp;
// Читаем данные и преобразуем из BCD в двоичную систему
temp = DS1307Read(0x00); // Чтение регистра секунд
second = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);
temp = DS1307Read(0x01); // Чтение регистра минут
minute = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);
temp = DS1307Read(0x02); // Чтение регистра часов
hour = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);

lcd_com(0xC4);
lcd_num_to_str(hour, 2); // Выводим на экран часы
lcd_com(0xC7);
lcd_num_to_str(minute, 2); // Выводим на экран минуты
lcd_com(0xCA);
lcd_num_to_str(second, 2); // Выводим на экран секунды

if((PINC & (1 << PC0)) == 0) // Если нажата кнопка
{
while((PINC & (1 << PC0)) == 0){} // Ждем отпускания кнопки
hour++; // Увеличиваем часы на 1
if(hour > 23) hour = 0;
// Преобразуем из двоичной системы в BCD и записываем в DS1307
uint8_t temp;
temp = ((hour/10) << 4)|(hour%10);
DS1307Write(0x02, temp);
_delay_ms(100);
}

if((PINC & (1 << PC1)) == 0) // Если нажата кнопка
{
while((PINC & (1 << PC1)) == 0){} // Ждем отпускания кнопки
minute++; // Увеличиваем минуты на 1
if(minute > 59) minute = 0;
// Преобразуем из двоичной системы в BCD и записываем в DS1307
uint8_t temp;
temp = ((minute/10) << 4)|(minute%10);
DS1307Write(0x01, temp);
_delay_ms(100);
}
}
} 

Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#6

Сообщение Gimpel »

Спасибо! Завтра буду разбираться)
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#7

Сообщение Gimpel »

Так. На атмеге8 всё работает. Как только выбираю атмега32 этот же самый код не работает. Ни исходный, ни вот этот без возврата ошибок. Почему-то на RTC бежит показание часов - они тикают как секунды, а минуты правильно показывает.
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#8

Сообщение AntonChip »

Кнопки поменяли на другие линии порта? У atmega32 на PC0 PC1 висит шина i2c.
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#9

Сообщение Gimpel »

AntonChip писал(а): 11 апр 2020, 18:21 Кнопки поменяли на другие линии порта? У atmega32 на PC0 PC1 висит шина i2c.
Спасибо! Заработало! Не ту частоту всё таки выставил :oops:
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#10

Сообщение Gimpel »

А как вывести на 7-сегментный индикатор данные из часов с помощью этого кода?
void DS1307_GetTime(byte *hours, byte *minutes, byte *seconds)
// Подпрограмма вернет часы, минуты, секунды в формате BCD.
{
*hours = I2C_ReadRegister(DS1307,HOURS_REGISTER);
*minutes = I2C_ReadRegister(DS1307,MINUTES_REGISTER);
*seconds = I2C_ReadRegister(DS1307,SECONDS_REGISTER);
}
Я до LCD ещё не дошёл. Пока пытаюсь работать с LED-индикатором.

Вот так почему-то не работает:
while(1)
{
unsigned int hour, second, minute;
// Читаем данные и преобразуем из BCD в двоичную систему
DS1307_GetTime(byte *hours, byte *minutes, byte *seconds)
second = (((seconds & 0xF0) >> 4)*10)+(seconds & 0x0F);
DS1307_GetTime(byte *hours, byte *minutes, byte *seconds)
hour = (((hour & 0xF0) >> 4)*10)+(hour & 0x0F);
DS1307_GetTime(byte *hours, byte *minutes, byte *seconds)
minute = (((minute & 0xF0) >> 4)*10)+(minute & 0x0F);
}
где hour, second, minute это переменные для вывода на LED-дисплей через прерывание по переполнению (как в примере с АЦП).
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#11

Сообщение AntonChip »

сделайте переменные глобальными, объявите их в начале кода
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#12

Сообщение Gimpel »

AntonChip писал(а): 12 апр 2020, 21:58 сделайте переменные глобальными, объявите их в начале кода
он у меня ругается на неправильную запись "void DS1307_GetTime(byte *hours, byte *minutes, byte *seconds)"
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#13

Сообщение AntonChip »

по этому куску кода не могу определить, надо весь код полностью
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#14

Сообщение Gimpel »

Работа с DS1307:

DS1307.c
#include "TWI.h"

#define DS1307 0xD0 // адрес шины I2C для микросхемы DS1307 RTC
#define SECONDS_REGISTER 0x00
#define MINUTES_REGISTER 0x01
#define HOURS_REGISTER 0x02
#define DAYOFWK_REGISTER 0x03
#define DAYS_REGISTER 0x04
#define MONTHS_REGISTER 0x05
#define YEARS_REGISTER 0x06
#define CONTROL_REGISTER 0x07
#define RAM_BEGIN 0x08
#define RAM_END 0x3F

void DS1307_GetTime(byte *hours, byte *minutes, byte *seconds)
// Подпрограмма вернет часы, минуты, секунды в формате BCD.
{
*hours = I2C_ReadRegister(DS1307,HOURS_REGISTER);
*minutes = I2C_ReadRegister(DS1307,MINUTES_REGISTER);
*seconds = I2C_ReadRegister(DS1307,SECONDS_REGISTER);
}
вывод на LCD-экран:
// LCD_Init инициализирует контроллер LCD
// LCD_Cmd посылает команду контроллеру
// LCD_Char посылает один символ ASCII на экран LCD
// LCD_Clear очищает экран LCD и возвращает курсор в исходную позицию
// LCD_Home возвращает курсор на экране LCD в исходную позицию
// LCD_Goto помещает курсор в позицию (x,y)
// LCD_Line помещает курсор в начало строки (x)
// LCD_Hex отображает шестнадцатиричное значение
// LCD_Integer отображает целочисленное значение
// LCD_String отображает строку
#include "settings.h"
#include "misc.h"

// Для подключения модуля LCD требуется 6 выводов портов GPIO: 2 для сигналов
// управления и 4 для передачи данных.
// Порт B используется для обмена данными с контроллером HD44780 LCD.
// Следующие определения указывают, к каким выводам портов микроконтроллера AVR
// подключен индикатор LCD:
#define LCD_RS 0 // ножка для LCD R/S (PB0)
#define LCD_E 1 // ножка для сигнала разрешения LCD
#define DAT4 2 // ножка для d4
#define DAT5 3 // ножка для d5
#define DAT6 4 // ножка для d6
#define DAT7 5 // ножка для d7

// Следующие определения задают команды для контроллера HD44780.
#define CLEARDISPLAY 0x01
#define SETCURSOR 0x80

void PulseEnableLine ()
// Генерирует импульс лог. 1 на выводе разрешения шины индикатора LCD.
{
SetBit(PORTB,LCD_E); // LCD enable переводится в лог. 1
_delay_us(40); // ожидание 40 мкс
ClearBit(PORTB,LCD_E); // LCD enable переводится в лог. 0
}

void SendNibble(byte data)
// Подпрограмма посылает ниббл через шину данных.
{
PORTB &= 0xC3; // 1100.0011 = очистка 4 линий данных
if (data & _BV(4)) SetBit(PORTB,DAT4);
if (data & _BV(5)) SetBit(PORTB,DAT5);
if (data & _BV(6)) SetBit(PORTB,DAT6);
if (data & _BV(7)) SetBit(PORTB,DAT7);
PulseEnableLine(); // этот импульс передает 4 бита в контроллер
}

void SendByte (byte data)
// Подпрограмма посылает байт через шину данных.
{
SendNibble(data); // послать старшие 4 бита
SendNibble(data<<4); // послать младшие 4 бита
ClearBit(PORTB,5); // включить светодиод LED
}

void LCD_Cmd (byte cmd)
// Подпрограмма посылает команду контроллеру LCD.
{
ClearBit(PORTB,LCD_RS); // сигнал R/S = 0, что означает данные команды
SendByte(cmd); // отправка команды
}

void LCD_Char (byte ch)
// Подпрограмма посылает символ контроллеру LCD для отображения.
{
SetBit(PORTB,LCD_RS); // сигнал R/S = 1, что означает данные символа
SendByte(ch); // отправка символа
}

void LCD_Init()
// Подпрограмма инициализирует контролер дисплея.
{
LCD_Cmd(0x33); // инициализация контроллера LCD
LCD_Cmd(0x32); // перевод контроллера в 4-битный режим шины данных
LCD_Cmd(0x28); // 2 строки, матрица символа 5x7
LCD_Cmd(0x0C); // выключить курсор (0x0E чтобы разрешить)
LCD_Cmd(0x06); // направление курсора: вправо
LCD_Cmd(0x01); // очистка дисплея
msDelay(3); // ожидание завершения инициализации LCD
}

void LCD_Clear()
// Подпрограмма очищает дисплей.
{
LCD_Cmd(CLEARDISPLAY);
msDelay(3); // ожидания завершения обработки команды LCD
}

void LCD_Home()
// Подпрограмма перемещает курсор LCD в "домашнюю" позицию
// (без очистки дисплея).
{
LCD_Cmd(SETCURSOR);
}

void LCD_Goto(byte x, byte y)
// Подпрограмма перемещает курсор LCD в указанную позицию экрана.
{
byte addr = 0; // строка 0 начинается с адреса 0x00
switch (y)
{
case 1: addr = 0x40; break; // строка 1 начинается с адреса 0x40
case 2: addr = 0x14; break;
case 3: addr = 0x54; break;
}
LCD_Cmd(SETCURSOR+addr+x); // обновить позицию курсора
}

void LCD_Line(byte row)
// Подпрограмма перемещает курсор LCD в начало указанной строки экрана.
{
LCD_Goto(0,row);
}

void LCD_String(const char *text)
// Подпрограмма отобразит строку на экране LCD в текущей позиции.
{
while (*text) // цикл, пока не появится символ /0
LCD_Char(*text++); // послать символ и обновить указатель
}

void LCD_Hex(int data)
// Подпрограмма отобразит HEX-значение данных на экране LCD
// в текущей позиции курсора.
{
char st[8] = ""; // массив для хранения результата
itoa(data,st,16); // конвертация числа в hex-строку ASCII
//LCD_Message("0x"); // если нужно, можно добавить префикс "0x"
LCD_String(st); // отобразить строку на LCD
}

void LCD_Integer(int data)
// Подпрограмма отобразит целое число на экране LCD
// в текущей позиции курсора.
{
char st[8] = ""; // массив для хранения результата
itoa(data,st,10); // преобразование числа в строку ASCII
LCD_String(st); // отобразить строку на LCD
}
и главная программа:
#include "settings.h"
#include "HD44780.h"
#include "TWI.h"
#include "DS1307.h"
#include "misc.h"

void ShowDevices()
// Сканирует шину I2C и отображает адреса всех найденных устройств.
{
LCD_Line(1); LCD_String("Found:");
byte addr = 1;
while (addr>0)
{
LCD_Char(' ');
addr = I2C_FindDevice(addr);
if (addr>0) LCD_Hex(addr++);
}
}

void LCD_TwoDigits(byte data)
// Вспомогательная функция для WriteDate().
// На входе получает 2 цифры в формате BCD.
// Выводит эти цифры на дисплей LCD в текущую позицию курсора.
{
byte temp = data>>4;
LCD_Char(temp+'0');
data &= 0x0F;
LCD_Char(data+'0');
}

void WriteDate()
{
byte months, days, years;
DS1307_GetDate(&months,&days,&years);
LCD_TwoDigits(months);
LCD_Char('/');
LCD_TwoDigits(days);
LCD_Char('/');
LCD_TwoDigits(years);
}

void WriteTime()
{
byte hours, minutes, seconds;
DS1307_GetTime(&hours,&minutes,&seconds);
LCD_TwoDigits(hours);
LCD_Char(':');
LCD_TwoDigits(minutes);
LCD_Char(':');
LCD_TwoDigits(seconds);
}

void LCD_TimeDate()
{
LCD_Line(0); WriteTime();
LCD_Line(1); WriteDate();
}

// ---------------------------------------------------------------------------
// Главный цикл программы.
void MainLoop()
{
while(1)
{
LCD_TimeDate(); // отображение на LCD времени и даты
msDelay(1000); // между обновлениями ожидание 1 секунда
}
}

// ---------------------------------------------------------------------------
// Основная программа: тело функции main.
int main(void)
{
InitAVR(); // настройка портов GPIO микроконтроллера
LCD_Init(); // инициализация контроллера HD44780 дисплея LCD
I2C_Init(); // настройка шины I2C (с установкой её тактовой частоты)
LCD_String("Ready.");
ShowDevices(); // показать, что шина I2C работает нормально
msDelay(4000);
LCD_Clear();
MainLoop(); // отображает время
}
LCD-экран работает с указателями "DS1307_GetTime(&hours,&minutes,&seconds);", которые я ещё не изучил (не добрался ещё до этого типа экрана, а в моргании диодом или АПЦ это не используется), поэтому я пытаюсь вывести данные с DS1307 на 7LED-дисплей. Не выходит. Две недели уже мучаюсь.
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#15

Сообщение AntonChip »

Код: Выделить всё

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

unsigned char SEGMENTE[] =
{
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F  // 9
};

uint8_t hours, minutes, seconds; 
volatile unsigned char seg_counter, adc_counter;
 
// Прерывание по переполнению T2, динамическая индикация
ISR(TIMER2_OVF_vect)
{  
PORTD = 0xFF; // Гасим все сегменты
PORTB = (1 << seg_counter); // Последовательно зажигаем общие аноды
     
switch(seg_counter)
{  
case 0:
PORTD = ~(SEGMENTE[hours/10]); // Выводим первый разряд
break;
case 1:
PORTD = ~(SEGMENTE[hours%10]); // Выводим второй разряд
break; 
case 2:
PORTD = ~(SEGMENTE[minutes/10]); // Выводим третий разряд
break;         
case 3:
PORTD = ~(SEGMENTE[minutes%10]); // Выводим четвертый разряд
break; 
}
if(seg_counter++ > 2) seg_counter = 0; 
}
 
// Функция инициализация шины TWI
void I2CInit(void)
{
TWBR = 2; // Настройка частоты шины
TWSR = (1 << TWPS1)|(1 << TWPS0); // Предделитель на 64
TWCR |= (1 << TWEN); // Включение модуля TWI
}

// Функция СТАРТ
void I2CStart(void)
{
TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTA); // Передача условия СТАРТ
while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
}

// Функция СТОП
void I2CStop(void)
{
TWCR = (1 << TWINT)|(1 << TWEN)|(1 << TWSTO); // Передача условия СТОП
while(TWCR & (1 << TWSTO)); // Ожидание завершения передачи условия СТОП
}

// Функция записи данных по шине
void I2CWriteByte(uint8_t data)
{
TWDR = data; // Загрузка данных в TWDR	
TWCR = (1 << TWEN)|(1 << TWINT); // Сброс флага TWINT для начала передачи данных
while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
}

// Функция чтения данных по шине
uint8_t I2CReadByte(uint8_t ack)
{
uint8_t data = 0;
// Возвращаем "подтверждение" после приема
if(ack) TWCR |= (1 << TWEA);
// Возвращаем "неподтверждение" после приема
// Ведомое устройство не получает больше данных
// обычно используется для распознования последнего байта
else TWCR &= ~(1 << TWEA);
// Разрешение приема данных после сброса TWINT
TWCR |= (1 << TWINT); 
while(!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
// Проверка статуса
// Если принят байт данных и возвращается "подтверждение"(0x50)
// или принят байт данных и возвращается "ненеподтверждение"(0x58)
if((TWSR & 0xF8) == 0x50 || (TWSR & 0xF8) == 0x58)
data = TWDR; // Читаем данные из TWDR 
return data; // OK
}

// Функция чтения данных из DS1307
uint8_t DS1307Read(uint8_t address)
{
uint8_t data = 0;
I2CStart(); // СТАРТ
I2CWriteByte(0b11010000);	// адрес DS1307+W
// Передача адреса необходимого регистра
I2CWriteByte(address);
I2CStart(); // Повторный СТАРТ	
I2CWriteByte(0b11010001);	// адрес DS1307+R
data = I2CReadByte(0);
I2CStop(); // СТОП
return data;
}

// Функция записи данных в DS1307
void DS1307Write(uint8_t address, uint8_t data)
{
I2CStart();	// СТАРТ
I2CWriteByte(0b11010000);	// адрес DS1307+W
// Передача адреса необходимого регистра
I2CWriteByte(address);
I2CWriteByte(data); // Запись данных
I2CStop(); // СТОП
}

void DS1307_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds)
{
uint8_t temp;
// Читаем данные и преобразуем из BCD в двоичную систему
temp = DS1307Read(0x00); // Чтение регистра секунд
*seconds = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);
temp = DS1307Read(0x01); // Чтение регистра минут
*minutes = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);
temp = DS1307Read(0x02); // Чтение регистра часов
*hours = (((temp & 0xF0) >> 4)*10)+(temp & 0x0F);
}
 
// Главная функция
int main (void)
{
// Настройка портов ввода/вывода
DDRB = 0xFF; // Выходы на общие аноды
PORTB = 0x00; // Ноль на выходе
DDRD = 0xFF; // Выходы на сегменты
PORTD = 0x00; // Ноль на выходе
 
// Настройка Т2
TIMSK |= (1 << TOIE2); // Разрешение прерывания по таймеру2
TCCR2 |= (1 << CS21);  // Предделитель на 8
 
sei(); // Глобально разрешаем прерывания

I2CInit(); // Инициализация шины I2C

// Запускаем ход часов
uint8_t temp;
temp = DS1307Read(0x00);
temp &= ~(1 << 7); // обнуляем 7 бит
DS1307Write(0x00,temp);
 
// Главный цикл
while(1)
{
DS1307_GetTime(&hours, &minutes, &seconds);
_delay_ms(1000); 
}
}

Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#16

Сообщение Gimpel »

Продолжаю мучить часы.

Хочу сделать мигание точки с частотой 1 Гц (на таймере АтМеги данную функцию реализовал, но я посчитал, что так делать расточительно потому, что ДС1307 обладает выводом, который может это реализовать). Открыл тех.описание микросхемы. За генерацию отвечает регистр №7 (адрес 0х07). Установку частоты мерцания в 1 Гц устанавливаем сбросом бит №0 и 1 (RS1, RS0). Записываю в регистр часов следующее:
0b00010000 - это включение бита №4 (SQWE) и задание частоты мигания в 1 Гц, как сказал выше битами 0 и 1. В итоге получаю не 1 Гц, а какую-то не понятную генерацию, но Протеус ни на что не ругается. В чём дело?
Вложения
1003.jpg
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#17

Сообщение AntonChip »

Попробуйте подтяните выход резистором к плюс питания
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#18

Сообщение Gimpel »

AntonChip писал(а): 20 апр 2020, 21:53 Попробуйте подтяните выход резистором к плюс питания
Сделал. Всё равно прыгает частота. Не пойму в чём дело. Я правильно ведь обратился к регистру и его настройки задал для 1 Гц верно?
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: I2C шина

#19

Сообщение AntonChip »

Gimpel писал(а): 21 апр 2020, 00:50 Сделал. Всё равно прыгает частота. Не пойму в чём дело. Я правильно ведь обратился к регистру и его настройки задал для 1 Гц верно?
Да, настройка регистра правильная
Аватара пользователя
Gimpel
Любитель
Сообщения: 22
Зарегистрирован: 29 июн 2019, 10:38

Re: I2C шина

#20

Сообщение Gimpel »

Разбираюсь.

Сейчас посмотрел какой у меня индикатор для часов лежит. Общий катод, 4 сегмента, красный. Что я хочу сделать. Хочу отделить значение часов и минут мигающей с частотой 1 Гц точкой. Мигание думаю обеспечить выводом SQWE с ДС1307. А как это сделать? Ведь если я подключаю точку на знакоместо №2 (отделение часов и минут), то мигают все точки индикатора - они же общие.
PORTD = SEGMENTE[hour % 10]|0x80; //добавляем точку
Ответить