Форум РадиоКот https://radiokot.ru/forum/ |
|
Atmega 328 и аппаратный I2C https://radiokot.ru/forum/viewtopic.php?f=57&t=194717 |
Страница 1 из 2 |
Автор: | p7070 [ Пт авг 30, 2024 11:32:22 ] |
Заголовок сообщения: | Atmega 328 и аппаратный I2C |
Всем привет! Решил просканировать шину 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 байта данных начинает процесс передачи } |
Автор: | shonty [ Пт авг 30, 2024 12:03:55 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
p7070 писал(а): PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился как это спасёт от отсутствия подтяжки на линии? ![]() |
Автор: | HardWareMan [ Пт авг 30, 2024 12:20:22 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
p7070 писал(а): PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился как это спасёт от отсутствия подтяжки на линии? ![]() Порт у МК устроен так, что когда он настроен на вход его регистр вывода своей лог.1 включает подтяжку к VCC. ![]() |
Автор: | shonty [ Пт авг 30, 2024 12:37:23 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
HardWareMan писал(а): Порт у МК устроен так, что когда он настроен на вход его регистр вывода своей лог.1 включает подтяжку к VCC. то есть внешняя подтяжка для TWI интерфейса для atmega328p не нужна? ![]() |
Автор: | p7070 [ Пт авг 30, 2024 12:57:37 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
p7070 писал(а): PORTC |=( 1<<PC5|1<<PC4); // Включим подтяжку на ноги, вдруг юзер на резисторы пожмотился как это спасёт от отсутствия подтяжки на линии? ![]() Не, не! не пожмотился - на отладочной плате 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: HardWareMan писал(а): Порт у МК устроен так, что когда он настроен на вход его регистр вывода своей лог.1 включает подтяжку к VCC. то есть внешняя подтяжка для TWI интерфейса для atmega328p не нужна? ![]() нужна - обязательно нужна |
Автор: | OKF [ Пт авг 30, 2024 13:09:08 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Бывало экономил. Работает и на внутренней подтяжке. |
Автор: | p7070 [ Пт авг 30, 2024 13:14:08 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Бывало экономил. Работает и на внутренней подтяжке. а при написании кода всегда библиотеками пользовался, или аппаратными средствами так же получалось? |
Автор: | OKF [ Пт авг 30, 2024 13:53:40 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Нет, без библиотек. Всё своё, по минимуму). А, извиняюсь.( I2C на мастере программный. |
Автор: | p7070 [ Пт авг 30, 2024 14:10:58 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Нет, без библиотек. Всё своё, по минимуму). А, извиняюсь.( I2C на мастере программный. можете мне пример из вашего готового с вариантом опроса шины avr скинуть в тексте кода? - я бы сравнил, что не так у меня . Даташит перевел, все прочитал и даже вроде все понятно, кроме того брал за основу готовый пример, где понимаю все "до винтика" - но в моем варианте не заработало |
Автор: | shonty [ Пт авг 30, 2024 14:25:40 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
p7070 писал(а): Были мысли, что хватаю данные из регистра состояния до того , как ведомый успел подать сигнал на шину и для этого еще одну задержку ввел после отправки адреса задержка вряд ли.. ACK на 9-м такте формируется.. p7070 писал(а): готовый пример ну точно, DI HALT же, судя по реплике))если что, то мой код на асме, и он один в один из даташита (Table 26-2) и без проверок статуса |
Автор: | OKF [ Пт авг 30, 2024 14:33:47 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Так софтовый же). И в/в по хитрому. Но логика должна быть понятна. 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; } |
Автор: | p7070 [ Пт авг 30, 2024 14:50:05 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Так софтовый же)...... Но логика должна быть понятна. [/code][/spoiler] бегло просмотрел - вечерком изучу внимательнее, но и с первого взгляда все читается - должен разобраться - записано удобно |
Автор: | HardWareMan [ Пт авг 30, 2024 15:56:59 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
то есть внешняя подтяжка для TWI интерфейса для atmega328p не нужна? ![]() В контексте TWI/I2C зависит от скорости, на которой хочется работать. Бывало экономил. Работает и на внутренней подтяжке. На какой скорости пробовали? Так софтовый же). И в/в по хитрому. Но логика должна быть понятна. А, софтовый, ок, понятно. Я тоже в одном проекте не захотел обрабатывать кучу ошибок в аппаратнос I2C (только уже на STM32F4) и написал простой программный мастер. Т.к. слейв один и это обычная флешка всё работает стабильно. Но я всё равно подстраховался и подтянул снаружи 4,7к. Скорости порядка 500кГц. |
Автор: | OKF [ Пт авг 30, 2024 16:58:25 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
На какой скорости пробовали? 100 кгц. ds1307 потому что.) |
Автор: | p7070 [ Пт авг 30, 2024 21:44:56 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
Даже не сомневаюсь, что будут работать примеры - уже проверял чужие реализации, но хочеться вернуться к вопросу,а то уже обсуждение пошло в самостоятельное плавание: есть фрагмент кода, который перебирает адреса, сдвигает их и формирует блок "адрес устройства + сигнал на чтение", все регистры настроены и есть старт + передача блока адреса в шину - но почему при передоре адресов не откликнулся ни один номер ? чьл не так? Код: 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: ВСЁ!!! нашёл ошибку - и исправил - код подтверждения получен - сейчас программу поправлю, чтоб при успешном отклике адрес устройства выводила |
Автор: | Starichok51 [ Сб авг 31, 2024 08:12:15 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
во-первых, в приведенном тобой коде проверки кода подтверждения не видать. во-вторых, где была ошибка?рассказывай, не интригуй нас. |
Автор: | Eats [ Сб авг 31, 2024 17:00:09 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
слейв один и это обычная флешка всё работает стабильно. Но я всё равно подстраховался и подтянул снаружи 4,7к. Скорости порядка 500кГц. У меня на 40 кГцах работало нестабильно с резисторами по 10к. Но там питание было грязное, а 3-вольтовый слейв понимал логическую единицу примерно от 2.7В и выше. Пришлось поставить 4.7к, причём линии клока оказалось достаточно. Осциллографом я видел сильно заваленные (дальше середины тактового импульса) фронты клока, которые стали заметно круче с резистором на 4к7. На 80 кГцах оно после этого заработало (по паспорту оно 100-килогерцовое), но мне так быстро и не надо было, 40 вполне достаточно. В общем, я бы на резисторах не экономил. На спичках больше сэкономишь! ![]() |
Автор: | p7070 [ Вс сен 01, 2024 07:34:26 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
во-первых, в приведенном тобой коде проверки кода подтверждения не видать. во-вторых, где была ошибка?рассказывай, не интригуй нас. какая уж тут интрига? сперва я частоту задранную понизил TWBR - было с ошибкой: Код: #define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/2) сделал: Код: #define TWBR_VALUE (((F_CPU)/(F_I2C)-16)/8) далее проверял TWSR не верно - хотя в даташите читал. что нужно без учета младших байтов (т.е. без предделителя) - и я поступил так: Код: uint8_t a=TWSR>>3; позже. когда перевел таблицу из даташита понял. почему код должен быть 0Х08 при отправке адреса. Нужно было исправит : Код: uint8_t a=TWSR&0xF8; а далее выводить адрес и значение TWSR: этосейчас стало просто , когда получилось - а вначале казалось сложно и непонятно - сейчас присупил к ведомому утсройству DS3231 и пока так же есть трудности.пробую без готовых библиотек по даташиту |
Автор: | Starichok51 [ Пн сен 02, 2024 18:07:23 ] |
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C |
p7070 писал(а): хотя в даташите читал. что нужно без учета младших байтов не байтов, а битов. байт - это все 8 битов регистра.p7070 писал(а): код должен быть 0Х08 при отправке адреса во-первых, 0х08 - это код старта, а не отправки адреса. когда ты перебираешь адреса, повторный старт дает код 0х10. поэтому нужно проверять оба кода. а при отправке адреса успешный код равен 0х18, а не успешный код адреса равен 0х20. и здесь нужно проверять на успешную отправку адреса и неуспешную.если на отправку адреса получишь 0х18, значит устройство найдено и ответило. а если получишь 0х20, то адрес был неверный. |
Автор: | p7070 [ Чт сен 05, 2024 21:59:44 ] | |||
Заголовок сообщения: | Re: Atmega 328 и аппаратный I2C | |||
[quote="p7070"] если на отправку адреса получишь 0х18, значит устройство найдено и ответило. а если получишь 0х20, то адрес был неверный. Да - перепроверил, причем выводил и по-парно "адрес устройства - статус" и после по услочию только адрес "откликнувшийся" на запрос, причем адреса уже знал и перебором по порядку еще раз их обнаружил ( ранее тест на ардуино) . Но это не всё - далее решил соединить уже проверенный код запуска дисплея 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){} }
|
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |