Страница 1 из 2
Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 11:32:22
p7070
Всем привет! Решил просканировать шину I2С и при этом не использовать ардуиновских продуктов - с ними и так все понятно и просто . Выбрал аппаратную реализацию и ...не могу найти проблему - сообщает код состояния шины 0х48 ( т.е. адрес отправлен и ACK не получен)
Код: Выделить всё
/*atmega 328
* 2708.cpp
*27/08/24 настроил uart
* Created: 27.08.2024 21:34:56
* Author : Павел
*/
#define F_CPU 8000000UL // Тактовая частота микроконтроллера
#include <avr/io.h>
#include <util/delay.h> // Библиотека программной задержки
#define BAUD 9600UL // Задание скорости обмена по UART
#define SPEED ((F_CPU+BAUD*8)/(BAUD*16)-1)
#define F_I2C 100000UL
#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/2)
void out_UART (uint8_t);
void Init_i2c(void) // Настройка режима мастера
{
PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился
DDRC &=~(1<<PC5|1<<PC4);
TWBR = TWBR_VALUE; // Настроим битрейт
TWSR = (0<<TWPS1)|(0<<TWPS0);
}
int main(void)
{
UCSR0B = (1<<TXEN0); // Настройка UART на передачу
UCSR0C = (0<<UMSEL00|1<<UCSZ01|1<<UCSZ00);
UBRR0L = SPEED & 0xFF;
UBRR0H = SPEED >> 8;
for(uint8_t adress=0;adress<127;adress++)
{
/*формируем состояние СТАРТ*/
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
/*выдаем на шину пакет SLA-R*/
TWDR = (adress<<1)|1;
TWCR = (1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
uint8_t a=TWSR;
//if(a==0x08){
while(!(UCSR0A&(1<<UDRE0))); // Ожидание готовности UART к передаче
UDR0 = a;
//}
//while(!(UCSR0A&(1<<UDRE0))); // Ожидание готовности UART к передаче
//UDR0 =0xFF;
_delay_ms(100);
}
/* Replace with your application code */
while (1)
{
}
}
////////////////////////////////////////////////////////////
// ПРИЕМ/ПЕРЕДАЧА ДАННЫХ UART
////////////////////////////////////////////////////////////
void out_UART(uint8_t data){ // Передача байта через UART
while(!(UCSR0A&(1<<UDRE0))); // Ожидание готовности UART к передаче
UDR0 = data; // Запись в регистр UDR байта данных начинает процесс передачи
}
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 12:03:55
shonty
p7070 писал(а):PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился
как это спасёт от отсутствия подтяжки на линии?

Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 12:20:22
HardWareMan
[uquote="shonty",url="/forum/viewtopic.php?p=4620442#p4620442"]
p7070 писал(а):PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился
как это спасёт от отсутствия подтяжки на линии?

[/uquote]
Порт у МК устроен так, что когда он настроен на вход его регистр вывода своей лог.1 включает подтяжку к VCC.

Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 12:37:23
shonty
HardWareMan писал(а):Порт у МК устроен так, что когда он настроен на вход его регистр вывода своей лог.1 включает подтяжку к VCC.
то есть внешняя подтяжка для TWI интерфейса для atmega328p не нужна?

Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 12:57:37
p7070
[uquote="shonty",url="/forum/viewtopic.php?p=4620442#p4620442"]
p7070 писал(а):PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился
как это спасёт от отсутствия подтяжки на линии?

[/uquote]
Не, не! не пожмотился - на отладочной плате PinBoard они есть и я их задействовал
Добавлено after 3 minutes 4 seconds:
Были мысли, что хватаю данные из регистра состояния до того , как ведомый успел подать сигнал на шину и для этого еще одну задержку ввел после отправки адреса
Добавлено after 1 minute 18 seconds:
еще вот тут поправил
Код: Выделить всё
#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/2)
делитель на 2 заменил на 8 - итого вышло :
Код: Выделить всё
#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/8)
Добавлено after 45 seconds:
[uquote="shonty",url="/forum/viewtopic.php?p=4620454#p4620454"]
HardWareMan писал(а):Порт у МК устроен так, что когда он настроен на вход его регистр вывода своей лог.1 включает подтяжку к VCC.
то есть внешняя подтяжка для TWI интерфейса для atmega328p не нужна?

[/uquote]
нужна - обязательно нужна
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 13:09:08
OKF
Бывало экономил. Работает и на внутренней подтяжке.
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 13:14:08
p7070
[uquote="OKF",url="/forum/viewtopic.php?p=4620465#p4620465"]Бывало экономил. Работает и на внутренней подтяжке.[/uquote]
а при написании кода всегда библиотеками пользовался, или аппаратными средствами так же получалось?
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 13:53:40
OKF
Нет, без библиотек. Всё своё, по минимуму). А, извиняюсь.( I2C на мастере программный.
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 14:10:58
p7070
[uquote="OKF",url="/forum/viewtopic.php?p=4620475#p4620475"]Нет, без библиотек. Всё своё, по минимуму). А, извиняюсь.( I2C на мастере программный.[/uquote]
можете мне пример из вашего готового с вариантом опроса шины avr скинуть в тексте кода? - я бы сравнил, что не так у меня . Даташит перевел, все прочитал и даже вроде все понятно, кроме того брал за основу готовый пример, где понимаю все "до винтика" - но в моем варианте не заработало
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 14:25:40
shonty
p7070 писал(а):Были мысли, что хватаю данные из регистра состояния до того , как ведомый успел подать сигнал на шину и для этого еще одну задержку ввел после отправки адреса
задержка вряд ли.. ACK на 9-м такте формируется..
p7070 писал(а):готовый пример
ну точно, DI HALT же, судя по реплике))
если что, то мой код на асме, и он один в один из даташита (Table 26-2) и без проверок статуса
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 14:33:47
OKF
Так софтовый же). И в/в по хитрому. Но логика должна быть понятна.
soft_i2c.h
Спойлер
Код: Выделить всё
// software i2c master (don`t check i2c bus state!)
#include "main.h"
#define I2C_SDA A,0,H
#define I2C_SCL A,1,H
#define INTERNAL_PULLUP 1
#define SCL_PULLUP 0
#define I2C_DELAY 1 //4 uS, standard mode, else nop, nop
#define DEVICE 0xd0 //DS1307
typedef uint8_t addr_t;
// return: true/false
void i2cInit();
bool i2cReadRandom(addr_t address, uint8_t *buffer);
bool i2cReadCurrentAddress(uint8_t *buffer);
bool i2cReadSequential(uint8_t *buffer, uint16_t size);
bool i2cReadBlock(addr_t address, uint8_t *buffer, uint16_t size);
bool i2cWriteByte(addr_t address, uint8_t data);
bool i2cWriteBlock(addr_t address, uint8_t *buffer, uint8_t size);
soft_i2c.cpp
Спойлер
Код: Выделить всё
#include "soft_i2c.h"
#include "ds1307.h" //BINARY_DATA
enum {LOW, HIGH};
enum {WRITE, READ}; // operation bit
enum {NAK, ACK}; // I2cRead(argument)
#if INTERNAL_PULLUP
#define set_sda_1() (in(I2C_SDA), pullup(I2C_SDA))
#define set_sda_0() (off(I2C_SDA), out(I2C_SDA) )
#if SCL_PULLUP
#define set_scl_1() (in(I2C_SCL), pullup(I2C_SCL))
#define set_scl_0() (off(I2C_SCL), out(I2C_SCL) )
#else
#define set_scl_1() on(I2C_SCL)
#define set_scl_0() off(I2C_SCL)
#endif
#else
#define set_sda_1() in(I2C_SDA)
#define set_sda_0() out(I2C_SDA)
#define set_scl_1() in(I2C_SCL)
#define set_scl_0() out(I2C_SCL)
#endif
static bool i2cWrite(uint8_t data);
// Initialize SCL/SDA pins and set the bus high - NOT USED!
// out: SDA = 1 SCL = 1 (open draw)
void i2cInit() {
#if INTERNAL_PULLUP
pullup(I2C_SDA);
#if SCL_PULLUP
pullup(I2C_SCL);
#else
off(I2C_SCL); out(I2C_SCL);
#endif
#else
off(I2C_SDA); // low level to output mode
off(I2C_SCL);
#endif
}
#if I2C_DELAY
void i2c_delay()
{
_delay_us(I2C_DELAY);
}
#else
#define i2c_delay() asm volatile( "nop \n nop \n" )
#endif
// set start condition, device address and mode
// out: SDA = 1 SCL = 0, if return true
static bool i2c_start(uint8_t device) { //+ R/W mode
uint8_t i = 0; //timeout
i2c_delay();
set_sda_1();
set_scl_1();
do // check i2c bus
if (!--i)
return false;
#if SCL_PULLUP
while (!pin(I2C_SDA) || !pin(I2C_SCL));
#else
while (!pin(I2C_SDA));
#endif
i2c_delay();
set_sda_0();
i2c_delay();
set_scl_0();
set_sda_1();
return i2cWrite(device);
}
// Stop condition, set the bus high
// out: SDA = 1 SCL = 1
static void i2c_stop() {
i2c_delay();
set_sda_0();
i2c_delay();
set_scl_1();
i2c_delay(); // tSU_STO: 4,0 0,6
set_sda_1();
i2c_delay();
} // tBUF: 4,7 1,3
// Read a byte, send Ack if more reads follow, Nak to terminate read
// in/out: SDA = 1 SCL = 0
static uint8_t i2cRead(uint8_t ack) {
uint8_t data = 0;
for (uint8_t i = 0; i < 8; i++) {
data <<= 1;
i2c_delay(); // tLOW: 4,7 1,3
set_scl_1();
if (pin(I2C_SDA))
data |= 1<<0;
i2c_delay(); // tHIGH: 4,0 0,6
set_scl_0();
}
set_sda_1();
if (ack)
set_sda_0();
i2c_delay(); // tLOW: 4,7 1,3
set_scl_1();
i2c_delay(); // tHIGH: 4,0 0,6
set_scl_0();
set_sda_1();
return data;
}
// Write a byte. Return: ACK == true
// in/out: SDA = 1 SCL = 0
static bool i2cWrite(uint8_t data) {
for (uint8_t m = 1<<7; m; m >>= 1) {
if (m & data)
set_sda_1();
else
set_sda_0();
i2c_delay(); // /2
set_scl_1();
i2c_delay(); // tHIGH: 4,0 0,6
set_scl_0();
}
i2c_delay(); // tLOW: 4,7 1,3
set_scl_1(); // get Ack or Nak
i2c_delay(); // tHIGH: 4,0 0,6
bool ack = pin(I2C_SDA);
set_scl_0();
set_sda_1();
return !ack;
}
// Write address
// in/out: SDA = 1 SCL = 0
static bool i2cWriteAddress(addr_t address) {
return i2cWrite(address);
}
//-----------------------------------------------
// in/out: SDA = 1 SCL = 1 (open draw) - the bus high
// return: false/true
// Read byte @Random address
bool i2cReadRandom(addr_t address, uint8_t *buffer) {
if (!i2c_start(DEVICE | WRITE))
return false;
if (!i2cWriteAddress(address))
return false;
return i2cReadCurrentAddress(buffer);
}
// Read byte @Current address
bool i2cReadCurrentAddress(uint8_t *buffer) {
if (!i2c_start(DEVICE | READ))
return false;
*buffer = i2cRead(NAK); //single byte
i2c_stop();
return true;
}
// Sequental Read @Current address
bool i2cReadSequential(uint8_t *buffer, uint16_t size) {
if (!i2c_start(DEVICE | READ))
return false;
do
*buffer++ = i2cRead(--size);
while (size);
i2c_stop();
return true;
}
// Read block
bool i2cReadBlock(addr_t address, uint8_t *buffer, uint16_t size) {
if (!i2c_start(DEVICE | WRITE))
return false;
if (!i2cWriteAddress(address))
return false;
return i2cReadSequential(buffer, size);
}
// Write byte
bool i2cWriteByte(addr_t address, uint8_t data) {
if (!i2c_start(DEVICE | WRITE))
return false;
if (!i2cWriteAddress(address))
return false;
bool ack = i2cWrite(data);
i2c_stop();
return ack;
}
// Write block
bool i2cWriteBlock(addr_t address, uint8_t *buffer, uint8_t size) {
bool ack;
if (!i2c_start(DEVICE | WRITE))
return false;
if (!i2cWriteAddress(address))
return false;
do
ack = i2cWrite(*buffer++);
while (--size);
i2c_stop();
return ack;
}
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 14:50:05
p7070
[uquote="OKF",url="/forum/viewtopic.php?p=4620491#p4620491"]Так софтовый же)...... Но логика должна быть понятна.
[/code][/spoiler][/uquote]
бегло просмотрел - вечерком изучу внимательнее, но и с первого взгляда все читается - должен разобраться - записано удобно
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 15:56:59
HardWareMan
[uquote="shonty",url="/forum/viewtopic.php?p=4620454#p4620454"]то есть внешняя подтяжка для TWI интерфейса для atmega328p не нужна?

[/uquote]
В контексте TWI/I2C зависит от скорости, на которой хочется работать.
[uquote="OKF",url="/forum/viewtopic.php?p=4620465#p4620465"]Бывало экономил. Работает и на внутренней подтяжке.[/uquote]
На какой скорости пробовали?
[uquote="OKF",url="/forum/viewtopic.php?p=4620491#p4620491"]Так софтовый же). И в/в по хитрому. Но логика должна быть понятна.[/uquote]
А, софтовый, ок, понятно. Я тоже в одном проекте не захотел обрабатывать кучу ошибок в аппаратнос I2C (только уже на STM32F4) и написал простой программный мастер. Т.к. слейв один и это обычная флешка всё работает стабильно. Но я всё равно подстраховался и подтянул снаружи 4,7к. Скорости порядка 500кГц.
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 16:58:25
OKF
[uquote="HardWareMan",url="/forum/viewtopic.php?p=4620509#p4620509"]На какой скорости пробовали?[/uquote]
100 кгц. ds1307 потому что.)
Re: Atmega 328 и аппаратный I2C
Добавлено: Пт авг 30, 2024 21:44:56
p7070
Даже не сомневаюсь, что будут работать примеры - уже проверял чужие реализации, но хочеться вернуться к вопросу,а то уже обсуждение пошло в самостоятельное плавание:
есть фрагмент кода, который перебирает адреса, сдвигает их и формирует блок "адрес устройства + сигнал на чтение", все регистры настроены и есть старт + передача блока адреса в шину - но почему при передоре адресов не откликнулся ни один номер ? чьл не так?
Код: Выделить всё
for(uint8_t adress=0;adress<127;adress++)
{
/*формируем состояние СТАРТ*/
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
/*выдаем на шину пакет SLA-R*/
TWDR = (adress<<1)|1;
TWCR = (1<<TWINT)|(1<<TWEN);
while(!(TWCR & (1<<TWINT)));
uint8_t a=TWSR;
while(!(UCSR0A&(1<<UDRE0))); // Ожидание готовности UART к передаче
UDR0 = a;
}
Добавлено after 44 minutes 34 seconds:
ВСЁ!!! нашёл ошибку - и исправил - код подтверждения получен - сейчас программу поправлю, чтоб при успешном отклике адрес устройства выводила
Re: Atmega 328 и аппаратный I2C
Добавлено: Сб авг 31, 2024 08:12:15
Starichok51
во-первых, в приведенном тобой коде проверки кода подтверждения не видать.
во-вторых, где была ошибка?рассказывай, не интригуй нас.
Re: Atmega 328 и аппаратный I2C
Добавлено: Сб авг 31, 2024 17:00:09
Eats
[uquote="HardWareMan",url="/forum/viewtopic.php?p=4620509#p4620509"]слейв один и это обычная флешка всё работает стабильно. Но я всё равно подстраховался и подтянул снаружи 4,7к. Скорости порядка 500кГц.[/uquote]У меня на 40 кГцах работало нестабильно с резисторами по 10к. Но там питание было грязное, а 3-вольтовый слейв понимал логическую единицу примерно от 2.7В и выше. Пришлось поставить 4.7к, причём линии клока оказалось достаточно. Осциллографом я видел сильно заваленные (дальше середины тактового импульса) фронты клока, которые стали заметно круче с резистором на 4к7. На 80 кГцах оно после этого заработало (по паспорту оно 100-килогерцовое), но мне так быстро и не надо было, 40 вполне достаточно. В общем, я бы на резисторах не экономил. На спичках больше сэкономишь!

Re: Atmega 328 и аппаратный I2C
Добавлено: Вс сен 01, 2024 07:34:26
p7070
[uquote="Starichok51",url="/forum/viewtopic.php?p=4620650#p4620650"]во-первых, в приведенном тобой коде проверки кода подтверждения не видать.
во-вторых, где была ошибка?рассказывай, не интригуй нас.[/uquote]
какая уж тут интрига? сперва я частоту задранную понизил TWBR - было с ошибкой:
Код: Выделить всё
#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/2)
сделал:
Код: Выделить всё
#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/8)
далее проверял TWSR не верно - хотя в даташите читал. что нужно без учета младших байтов (т.е. без предделителя) - и я поступил так:
позже. когда перевел таблицу из даташита понял. почему код должен быть 0Х08 при отправке адреса. Нужно было исправит :
а далее выводить адрес и значение TWSR:
этосейчас стало просто , когда получилось - а вначале казалось сложно и непонятно - сейчас присупил к ведомому утсройству DS3231 и пока так же есть трудности.пробую без готовых библиотек по даташиту
Re: Atmega 328 и аппаратный I2C
Добавлено: Пн сен 02, 2024 18:07:23
Starichok51
p7070 писал(а):хотя в даташите читал. что нужно без учета младших байтов
не байтов, а битов. байт - это все 8 битов регистра.
p7070 писал(а):код должен быть 0Х08 при отправке адреса
во-первых, 0х08 - это код старта, а не отправки адреса. когда ты перебираешь адреса, повторный старт дает код 0х10. поэтому нужно проверять оба кода. а при отправке адреса успешный код равен 0х18, а не успешный код адреса равен 0х20. и здесь нужно проверять на успешную отправку адреса и неуспешную.
если на отправку адреса получишь 0х18, значит устройство найдено и ответило.
а если получишь 0х20, то адрес был неверный.
Re: Atmega 328 и аппаратный I2C
Добавлено: Чт сен 05, 2024 21:59:44
p7070
[uquote="Starichok51",url="/forum/viewtopic.php?p=4621446#p4621446"][quote="p7070"]
если на отправку адреса получишь 0х18, значит устройство найдено и ответило.
а если получишь 0х20, то адрес был неверный.[/uquote]
Да - перепроверил, причем выводил и по-парно "адрес устройства - статус" и после по услочию только адрес "откликнувшийся" на запрос, причем адреса уже знал и перебором по порядку еще раз их обнаружил ( ранее тест на ардуино) . Но это не всё - далее решил соединить уже проверенный код запуска дисплея 1602, чтоб не мониторить посылки по терминалу ..но столкнулся с новой проблемой и пришлось вернуться к коду с дисплеем - не могу решить поблему (используя готовую библиотеку) вывод второй строки только при первом запуске и далее только после снятия питания и повторного подключения. Однако при перезапуске процессора кнопкой резет - вторая строка не выводится на дисплей. Порядок - заливка программы - вывод двух строк, далее резет и загружена только одна строка , если выкл и вновь вкл питание - две строки.
Код: Выделить всё
#define F_CPU 8000000UL
//#define Line " - %d"
//#define BAUD 9600UL // Задание скорости обмена по UART
//#define SPEED ((F_CPU+BAUD*8)/(BAUD*16)-1)
//#define F_I2C 100000UL
//#define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/8)
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <LCD.h>
int main(void) {
lcdInit();
_delay_ms(1000);
lcdClear();
lcdSetDisplay(LCD_DISPLAY_ON);
lcdSetCursor(LCD_CURSOR_ON);
_delay_ms(100);
char text[17];
strcpy(text, "PRINT");
lcdGotoXY(1, 0);
lcdPuts(text);
_delay_ms(1000);
//lcdClear();
strcpy(text, "TEXT");
lcdGotoXY(0, 8);
lcdPuts(text);
_delay_ms(1000);
while (1){}
}