Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Реклама ⓘ

AvrStudio 4. Библиотека для AVR. Модуль для I2C или TWI

Введение

Для Arduino написано много библиотек бери да пользуйся, не исключение и шина  I2C. А если посмотреть в сторону тех кто пишет на AvrStudio, то будет голое поле. Библиотеку писал на основе открытой библиотеки которую я скачал с сайта http://www.procyonengineering.com.

Информации про интерфейс в интернете много, а библиотек для работы с ним нет. Только GitHub в помощь. 

Несколько слов про интерфейс. Интерфейс, по-философии, чем-то похож на интерфейс 1-wire. Только для 1-wire синхронизация идет по времени и сигналам, а тут только по отдельной линии. Привожу таблицу сравнения

  I2C 1-Wire
сброс линии нет есть
стартовый бит есть есть
адрес устройства есть есть
Сигнал присутствия есть есть
стоповый бит есть нет

Лично для меня реализация интерфейса показалась достаточно сложной, объемной. Плюс данного интерфейса высокая скорость и возможность коммуникации между многими устройствами, что является не маловажным. Своего рода TCP/IP для мира микроконтроллеров. Можете посмотреть ради интереса фолианты по TCP/IP.

Подключение библиотеки

Перейдем к цели, а именно рассказать про использование библиотеки. Библиотека состоит из 2 файлов: i2c.h и i2c.c.

1. В начале необходимо скинуть эти 2 файла в папку с проектом.

2. Подключить в главном си-файле заголовочный файл: #include <i2c.h>

#include <avr/io.h>
#include <i2c.h>
int main(void)
{
while(1)
{
}
}

3. Затем необходимо открыть опции конфигурирования проекта: Project-->Configuration Option

3.1. В вкладке General необходимо в строке Frequency указать частоту тактирования контроллера . ВНИМАНИЕ: если это не сделать проект будет выдавать ошибку.

3.2.  Затем необходимо Включить корневую папку проекта в поиск Стандартных библиотек. Для этого во вкладке Include Directories нажимают кнопку New(Insert) и выбирают папку проекта.

3.3. Последний этап это включение файла i2c.c в проект. Для этого в дереве проекта на папке Source File кликают 2 раза ПКМ и выбирают пункт Add Existing File(s) и добавляют файл i2c.c. После этого проект должен собраться.

Библиотека может работать в режиме Мастер или Slave. В библиотеке можно подключить/отключить любой режим.

По-умолчанию библиотека собирается в режиме Master.

Описание концепции работы библиотеки

Иногда скачаешь библиотеку, и думаешь как ей пользоваться? А потом решаешь свою написать. Поэтому опишу почему пришел к такой структуре библиотеки. Библиотеку начал писать для работы с LCD2004 по I2C. Разных чипов работающих по I2C навалом, но все они работают по принципу EEPROM памяти. В начале Мастер посылает пакет в котором записывает в один или два байта адрес ячейки с которой хочет начать чтение данных в памяти, а затем Начинает их читать. Интерфейс я бы сказал ОЧЕНЬ ВЕЖЛИВЫЙ, это тебе не UART. Хочу передаю, хочу не передаю и ответственности за приемник никакой. Тут же Мастер интересуется кому посылает, готов сам откликнуться на чужую нужду и бросить свое дело. Ведомый же всегда скажет если дома, если есть место честно об этом скажет, а если не может больше принять данных, то тоже сообщит.

Рассмотрим запись и чтение данных из EEPROM памяти at24c512. Схема подключения представлена на рисунке.

  

В данном случае at24c512 имеет адрес устройства 0b1010000. Дебаггер для I2C присоединил для удобства. В таблице привожу общение контроллера с памятью. Само общение можно посмотреть с помощью Дебаггера. Каждая строка в таблице коммуникации это попадание в прерывание для Master.

Вот так выглядит таблица ячеек памяти для at24c512 после записи.

Номер ячейки памяти

Значение

Ячейки памяти

Нет адреса

Старший байт адрес (0x00)

Нет адреса

Младший байт адрес (0x03)

0x0000

0xFF

0x0001

0xF0

0x0002

0xF1

0x0003

0xFF

0xXXYY

0xZZ

-----------------------

-----------------------

0xFFFE

0xFF

0xFFFF

0xFF

Перед нами стоит следующая задача. Записать два байта 0xF0 и 0xF1 в ячейки памяти с индексами 0x0001 и 0x0002 соответственно. Для этого мы в начале отправляем адрес устройства с командой записи, а затем посылаем данные. Первые 2 байта это индекс первой ячейки для записи, а все последующие это данные которые последовательно будут записываться в ячейки памяти. Пока ячейки не кончаться Память будет посылать готовность к записи.

А ниже я привожу таблицу обмена между контроллером и памятью at24c512

Посылка Байт+(N)Ask

Номер прерывания при выполнении для Master

Действие Master

Описание события Master после наступления прерывания

Действие Slave

Описание действия Slave

START-бит

0x08

S

Мастер посылает  СТАРТ-бит

Мастер инициировал сеанс связи

Нет действий

Логика Slave готовиться к принятию адреса и сравнению со своим

0b1010000W_Ask

0x18

0b10100000

Мастер отправил адрес Slave для записи

Мастер отправил адрес Slave и получил Ask

Ask

Логика Slave приняла свой адрес для записи и отправила готовность к записи

0x00_Ask

0x28

0x00

Мастер отправил старший адрес 0x00 ячейки памяти для at24c512.

Мастер отправил данные и получил Ask, т.е. готовность к дальнейшему общению

Ask

Slave записал старший адрес ячейки памяти и готов дальнейшему общению

0x01_Ask

0x28

0x01

Мастер отправил младший адрес 0x01 ячейки памяти для at24c512.

Мастер отправил данные и получил Ask, т.е. готовность к дальнейшему общению

Ask

Slave записал младший адрес ячейки памяти и готов дальнейшему общению. Теперь следующий байт будет записан в ячейку с номером 0x0001

0xF0_Ask

0x28

0xF0

Мастер отправил данные для Slave

Мастер отправил данные и получил подтверждение для дальнейшей передачи

Ask

Slave записал данные (0xF0) в ячейку памяти с номером 0x0001. Номер адреса ячейки памяти увеличен на 1.

0xF1_Ask

0x28

0xF1

Мастер отправил данные для Slave

Мастер отправил данные и получил подтверждение для дальнейшей передачи

Ask

Slave записал данные (0xF1) в ячейку памяти с номером 0x0002. Номер адреса ячейки памяти увеличен на 1.

Stop

Нет прерывания

Stop

Мастер закончил отправку данных и посылает Стоп-бит

После отправки СТОП Мастер не попадает в прерывание

Нет действия

Slave прекращает сеанс общения

Следующая задача обратная. Считать значения ячеек памяти по адресам 0x0001 и 0x0002.

В начале Мастер посылает адрес Slave памяти с битом ЗАПИСИ, а затем 2 байта для записи адреса ячейки памяти Slave. После этого посылаем Повторный СТАРТ и снова Адрес Slave c битом ЧТЕНИЯ. И начинаем читать данные. Если мы прочитал последний байт, то Мастер не должен отправлять ASK. После этого Мастер посылаем СТОП. Сеанс общение закончился.

Посылка Байт+(N)Ask

Номер прерывания при выполнении для Master

Действие Master

Описание события Master после наступления прерывания

Действие Slave

Описание действия Slave

START-бит

0x08

S

Мастер посылает  СТАРТ-бит

Мастер инициировал сеанс связи

Нет действий

Логика Slave готовиться к принятию адреса и сравнению со своим

0b1010000W_Ask

0x18

0b10100000

Мастер отправил адрес Slave для записи

Мастер отправил адрес Slave и получил Ask

Ask

Логика Slave приняла свой адрес для записи и отправила готовность к записи

0x00_Ask

0x28

0x00

Мастер отправил старший адрес 0x00 ячейки памяти для at24c512.

Мастер отправил данные и получил Ask, т.е. готовность к дальнейшему общению

Ask

Slave записал старший адрес ячейки памяти и готов дальнейшему общению

0x01_Ask

0x28

0x01

Мастер отправил младший адрес 0x01 ячейки памяти для at24c512.

Мастер отправил данные и получил Ask, т.е. готовность к дальнейшему общению

Ask

Slave записал младший адрес ячейки памяти и готов дальнейшему общению. Теперь следующий байт будет записан в ячейку с номером 0x0001

ReSTART-бит

0x10

S

Мастер посылает  СТАРТ-бит

Мастер инициировал сеанс связи

Нет действий

Логика Slave готовиться к принятию адреса и сравнению со своим

0b1010000R_Ask

0x40

0b1010000R

Мастер отправил адрес Slave c битом ЧТЕНИЯ

Мастер получил подтверждения готовности Slave к чтению

Ask

Slave отправил готовность чтению. Передачи данных начинается с загруженного адреса (0x0001)

0xF0_Ask

0x50

Ask

 

Мастер отправил подтверждение, т.е запрос на чтение следующего байта

0xF0

Slave отправил данные из ячейки 0x0001, увеличил счетчик на 1, принял запрос на передачу следующего байта

0xF1_NAsk

0x58

NAsk

 

Мастер НЕ отправил ПОДТверждение, т.е запрос Мастер прочитал все необходимые байты.

0xF1

Slave отправил данные из ячейки 0x0002, увеличил счетчик на 1. Slave не принял подтверждения. Ожидает СТОП-бит.

Stop

Нет прерывания

Stop

Мастер закончил отправку данных и посылает Стоп-бит

После отправки СТОП Мастер не попадает в прерывание

Нет действия

Slave прекращает сеанс общения

Теория программирования

С теорией интерфейса мы закончили, а как же это реализовать обмен с помощью библиотеки? Опишу вкратце значение функций для режима Master.

Инициализация, естественно, библиотеки.

void i2cInit(void);

Загрузка данных для отправки в виртуальный Мастер буфер

 uint8_t i2cMasterUploadBuf(uint8_t data);

Мы последовательно записываем данные в буфер, пока место не кончиться. Если байт записался, то выдаем нам 1, иначе 0. В это случае байты не записываются.

А если мы записала байты, а потом поняли что не то записал, что делать? Для этого есть функция сброса данных о загруженных байтах. Т.е. байт загруженные остались, а библиотека думает что передавать нечего.

void i2cMasterBufReset(void);

Данные загрузили, теперь надо отправить их адресату

uint8_t i2cMasterSendBuf(uint8_t deviceAdd);

А если надо данные принять, то в начале загружаем 2 байта адреса ячеек памяти в буфер и вызываем функцию передачи по адресу устройства в количество N - байт.

uint8_t i2cMasterReceive(uint8_t deviceAdd, uint8_t lenght);

Данные считали, теперь надо их получить для этого есть спец функция для считывания значений из Мастер буфера по индексам. Если данная ячейка существует, то результат функции будет 1, иначе 0.

uint8_t i2cMasterDownloadBufIndex(uint8_t* result, uint8_t index);

Максимальный объем памяти Мастер буфера 255 байт.

Практическое задание

Давайте реализуем такое задание. У нас будет 2 кнопки. Задача одной из них записывать последовательно 2 байта в EEPROM память at24c512 в ячейку 0х0001 при нажатии начиная с 0x00, 0x01, затем 0x02, 0x03 и т.д.  

Задача 2 кнопки это считывание значений и вывод их на на светодиоды 2 портов.

Вот схема из протеуса

Листинг программы с комментариями

//=====================================================

#include <avr/io.h>
#include <i2c.h>

void init(void)
{
DDRA=0xFF; // настроили на вывод
DDRB=0xFF; // настроили на вывод
PORTA=0;   // Подтянули к земле 
PORTB=0;   // Подтянули к земле
DDRD=0x00; // настроили на вход
PORTD=0xFF;// подтянули к питанию
}
#define ADDRES_at24c512 0b1010000
static volatile uint8_t temp=0x00,a[2];
int main(void)
{
init();
i2cInit(); // инициализация шины
while(1)
{

if ((PIND&0b1000)==0) 
				{
				// Ожидаем когда отпустят кнопку 
				while(!(PIND&0b1000));
				// Загружаем старший адрес ячейки памяти
				i2cMasterUploadBuf(0x00);
				// Загружаем старший адрес ячейки памяти
				i2cMasterUploadBuf(0x01);
				// Загружаем данные для записи
				for(uint8_t i=0;i<2;i++)
				{
				i2cMasterUploadBuf(temp);
				temp++;
				}
				// Отправляем данные на запись
				i2cMasterSendBuf(ADDRES_at24c512);
				}

if ((PIND&0b10000000)==0) 
				{
				// Ожидаем когда отпустят кнопку 
				while(!(PIND&0b10000000));
				// Загружаем старший адрес ячейки памяти
				i2cMasterUploadBuf(0x00);
				// Загружаем старший адрес ячейки памяти
				i2cMasterUploadBuf(0x01);
				// Читаем данные из At24c512, кол-во 2 байта
				i2cMasterReceive(ADDRES_at24c512, 2);
				// копируем значения из Мастер-буфера
				for(uint8_t i=0;i<2;i++)
				i2cMasterDownloadBufIndex((uint8_t*)&a[i] , i);
				// Загружаем данные в барометры
				PORTA=a[0];
				PORTB=a[1];
				}
}
}

//================================================================

Заключение

Сама шина реализуется методом конечного автомата. Поэтому библиотека очень похожа на АК-47, а буфер на рожок с патронами. В начале вы забиваете рожок патроны (байты). Объем рожка - это размер буфера. Потом прицеливаетесь (вводите адрес), и стреляете пока не кончаться патроны (байты), или не будет осечки (Slave не может принять байты). Кол-во попаданий (загруженных байт) будет результатом функции.

P.S. В библиотеке реализована поддержка Slave режима. Если кому-то будет интересна данная тема могу написать статью. Там используется тот же подход, что и при работе с EEPROM памятью. Если при использовании встретите какие-либо ошибки пишите в комментариях или сообщениях. 

Прикрепленные файлы:

Теги:

Опубликована: 0 0
Я собрал 0 Участие в конкурсе 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (51) | Я собрал (0) | Подписаться

0
Публикатор #
На форуме автоматически создана тема для обсуждения статьи.
Ответить
+1
Oleg Kostetsky #
Библиотеку подключил, работает. Atmega324. Спасибо за сэкономленное время.
Ответить
0

[Автор]
Aleksey1408 #
Рад что помог, библиотеку писал для atmega16/32
Ответить
+1
Владимир #
Тестировал мастера в IAR среде.
Понравилась концепция и ... Всё отлично работает.
Спасибо за библиотеку.
Ответить
0

[Автор]
Aleksey1408 #
Я очень долго думал, как реализовать грамотно и удобно
Ответить
0
Владимир Мотягин #
Помогите подружить атмегу и I2C LCD с помощью этой библиотеки?
Пытаюсь изучить атмеловский Си
Ответить
0

[Автор]
Aleksey1408 #
Да без проблем. У меня есть даже готовая библиотека. https://github.com/acc1408/atmega16_osa
Вы можете написать мне письмо, и я через скайп покажу как работать с библиотекой
Ответить
0
zlk #
А кто такой avr_cmsis.h, который инклюдид скачанная библиотека?
Ответить
0

[Автор]
Aleksey1408 #
Я делал что-то наподобие фремворка своего для avr. А это был главный файл. Я много чего там писал. Почти всю переферию. Можете посмотреть на гитхабе. https://github.com/acc1408/atmega16_osa
Ответить
0
Vlad #
Чего то если в Протеусе выбираю EEPROM 24C01 или 02 или любую отличающуюся от 24С512, то пишется в память не в те ячейки и читается только FF FF
Ответить
0

[Автор]
Aleksey1408 #
Во-первых проверьте что у вас подключен к земле пин WP и вы можете записывать ячейки, во вторых, адрес ячеек в 24C01 программируется не 2 байтами, а только одним.
Ответить
0
Vlad #
WP подключен к земле.
Скорее всего Ваша вторая рекомендация "не 2 байтами а только одним"
А что в прошивке поменять в Вашем же проекте?
Вы их вроде как по одному и пишете?
// Загружаем старший адрес ячейки памяти
i2cMasterUploadBuf(0x00);
Это первый?
// Загружаем старший адрес ячейки памяти
i2cMasterUploadBuf(0x01);
Второй?
// Загружаем данные для записи
for(uint8_t i=0;i
Ответить
0

[Автор]
Aleksey1408 #
Необходимо первый байт заменить с 00 на 01. в строчке 27 и 45 и все должно заработать.
А еще возможно надо проверить адрес, посылки, возможно к земле у вас не прижаты А2 А1 и А0. Я смогу посмотреть проект 16 числа когда будет время. Даже можем по скайпу пообщаться
Ответить
0
Vlad #
Сменить с 00 на 01 не помогло. Помогло закоментить строку 29 и 47
// i2cMasterUploadBuf(0x01);
Вот теперь бы понять почему.....
P.S. Спасибо за весь Ваш материал какой тут представлен. Реально помогает понять и разобраться . И спасибо, что делитесь своими знаниями с другими. Знание приобретает силу только если им делиться.
Прикрепленный файл: mem.PNG
Ответить
0
Александр #
Здравствуйте, Алексей! Подскажите пожалуйста. Почему у меня не получается больше 18 байт записать за один раз и больше 20 байт прочитать тоже не получается.
AvrStudio 4. Библиотека для AVR. Модуль для I2C или TWI
Ответить
0

[Автор]
Aleksey1408 #
Добрый день. По идее все должно писаться. Cкорее всего буфер надо расширить в файле i2c.h, я сейчас не помню его размер по умолчанию.
Ответить
0
Александр #
Да, спасибо за ответ там по 20 выставлено. Я не посмотрел, думал 255.
Ответить
0

[Автор]
Aleksey1408 #
Чаще всего интерфейс применяться для различных датчиков, где не требуется большого кол-во данных для считывания за 1 раз. Оперативная память контроллера очень маленькая, поэтому ее необходимо тратить разумно.
Отредактирован 24.07.2020 11:37
Ответить
0
Александр #
Алексей, ещё раз спасибо за библиотеку! Я сделал в проекте часы с её помощью подключил LCD дисплей через PCF8574 к Atmega8 с настройкой часов одной кнопкой. Работает.
Ответить
0

[Автор]
Aleksey1408 #
Пожалуйста. Мне очень приятно, что мой труд оказался востребованным
Ответить
0
tehcom11 #
Здравствуйте Aleksey1408!
Можете объяснить как использовать функций в режиме Slave?
Ответить
0
tehcom11 #
Не могу понять как принять данные от мастера и как отправить мастеру?
Ответить
0

[Автор]
Aleksey1408 #
Мастер сам отправляет данные и инициирует приём. То что что вы говорите это режим slave.
Отредактирован 25.02.2021 10:29
Ответить
0
tehcom11 #
Наверно некорректно задал вопрос, я пытаюсь соединить два микроконтроллера.
Протокол для меня новый пока не все понятно.
Задачу я поставил следующую:
На мк мастера подключены две кнопки при нажатии на одну из них отправляется команда на мк слейва, на слейве должен загореться один из двух светодиодов , а при повторном нажатии кнопки на мастере светодиод на слейве должен погаснуть.
Ответить
0

[Автор]
Aleksey1408 #
Для этого необходимо раскоментировать определение slave. На счёт шины i2c. Задача шины синхронизировать байты озу мастера и карту памяти slave устройства. Будет желание могу по скайпу рассказать.
Ответить
0
tehcom11 #
Раскоментировать слейв это понятно. Не разобрался с функциями слейва поскольку еще не совсем понял как этот протокол работает.
По скайпу желание есть.
Ответить
0

[Автор]
Aleksey1408 #
Вы в proteus работаете. В какой версии?
Ответить
0
tehcom11 #
Установил как раз по случаю, 8ой с чем-то.
Ответить
0

[Автор]
Aleksey1408 #
Нужно точно знать
Ответить
0
tehcom11 #
Установлен протеус 8.10
Ответить
0
kab1561 #
Библиотека, конечно, хорошая, но при ее использовании с контроллером ATmega168(РА) возникли проблемы. Вроде блоки TWI для mega168 и mega16 полностью совпадают, а не работает. Начал разбираться и в Вашей библиотеке i2c.c нашел ошибку в функции передачи slave адреса в шину. Зачем полученный slave адрес сдвигать влево на 1 бит до передачи в функциях i2cMasterSendBuf и i2cMasterReceive?

Короче, тем кто будет использовать эту библиотеку необходимо в файле i2c.c заменить содержимое строки 339 на следующее:
i2cDeviceAddrRW=deviceAdd; // Загружаем Slave АДРЕС+W
а также строки 362 на следующее:
i2cDeviceAddrRW=deviceAdd | 1;
Ответить
0

[Автор]
Aleksey1408 #
Спасибо за уточняющий комментарий. Т.к. Slave-Адрес 7-битный, то можно его записать со смещением вправо или влево. В моей библиотеке я адрес смещен вправо. Вы же записывали его со смещением влево. Смещение вправо сделано для безопасности. То что предлагает вы. Адрес-Запись ХХХХХХХ0, А если человек ошибся и в конце записал 1, тогда согласно вашему коду адрес отправиться ХХХХХХ1, что соответствует чтению. А потом попробуй догадайся в чем подвох. Если Вам нравиться смещение вправо, тогда для кодобезопасности адрес-записи лучше написать так i2cDeviceAddrRW=deviceAdd&(~0x01); В этом случае последний бит будет гарантированно обнулен.
Ответить
0
kab1561 #
Добрый день, Алексей!
Подождите, давайте разберемся.
В даташитах производители указывают КОНКРЕТНЫЙ адрес микросхемы (или семейство адресов).
В Вашем примере адрес at24c512 Вы указываете как 0b1010000 (это правильный адрес микросхемы по даташиту) без всякого смещения! Согласно логики работы описанной здесь библиотеки (файл i2c.c) контроллер отправит в шину запрос к slave адресу 0b0100000 (разницу чувствуете?!) и будет домогаться устройства именно с этим адресом.

Мне для этого понимания понадобился день. В программной реализации интерфейса I2C (обычный ногодрыг) все подключенные slave микросхемы отлично работают, а с Вашей библиотекой какую-то ахинею получаю. Тренировался я на TDA7313 (slave адрес 0b10001000) и позже на INA226 (slave адрес 0b10000000). Прикол в том, что TDA7313 вообще никак не реагировала, а INA226 присылала какие-то рандомные куски данных со своих внутренних регистров, чем и путала.
Ответить
0

[Автор]
Aleksey1408 #
Доброго времени суток.
Я указал 7 битный адрес 0b_101_0000, но любой байт является 8-битным. Поэтому он храниться как 0b0101_0000, следовательно после смещения влево адрес преобразуется в 0b1010_0000, а вовсе не в 0b0010_0000 как написали вы. Еще один момент вы указываете 8-битный адрес микросхемы TDA7313 - 0b1000_1000, но I2C это 7 битный адрес, последний бит определяет режим работы шины чтение или запись.
Ответить
0
tehcom11 #
Не могу придумать решение такого вопроса:
Два микроконтроллера подключены межу собой, один слейв другой мастер. В контроллере в режиме слейва крутится функция i2cSlaveDownloadBuf() принимает байт данных (число) и выводит его на дисплей. После разрыва связи с мастером (механический разрыв - отсоединение кабеля) в функции i2cSlaveDownloadBuf() остается последнее принятое значение от мастера и сбросить его никак не получается. Как его сбросить?
Отредактирован 08.10.2023 05:49
Ответить
0

[Автор]
Aleksey1408 #
Основная задача I2C шины это синхронизация карты регистров. I2C шина разрабатывалась не для кабельного соединения, а для соединения микросхем на одной плате. I2C шина склонна зависать при разрыве соединения, особенно при разрыве во время передачи или приема.
Как вижу решения вопроса я.
Мастер передает n-байт и в n-байте содержится число, например, 0xFF. Контроллер Slave считывает n-байт. И если в нем содержится 0xFF, то пришли новые данные. Мы тестируем n-байт на 0xFF, когда он пришел, мы функцией i2cSlaveDownloadBuf( result, n)
считываем последний байт, если он 0xFF, то мы сбрасываем его функцией i2cSlaveUploadBuf(0x00, n)

uint8_t result[1];
uint8_t n=11; //например если длина посылки 11 байт, 11-ый байт содержит флаг обновления данных

while(1)
{
i2cSlaveDownloadBuf( result, n)
if (result[0])
{
// сбрасываем байт принятых новых данных
i2cSlaveUploadBuf(0x00, n)
// считываем новые данные из буфера.
}
}
Ответить
0
tehcom11 #
Почему когда я делаю такую запись:

uint8_t i = 0;
uint8_t z = 0;
while (1)
{
i2cSlaveDownloadBuf(&i, 1);
i2cSlaveDownloadBuf(&z, 2);
....код.....
....код.....
i2cSlaveUploadBuf(0, 1);
i2cSlaveUploadBuf(0, 2);
}
Переменные i и z остаются пустые - не срабатывает не одно условие.
Если i2cSlaveUploadBuf закомментировать то код работает?
Отредактирован 18.03.2024 22:51
Ответить
0

[Автор]
Aleksey1408 #
Причина в том, что обе функции используют один массив, а не буфер. И когда вы запускаете i2cSlaveUploadBuf(0, 1); он записывает на место 1 индекса массива число 0, а поэтому он затирает любые данные.
Ответить
0
tehcom11 #
Но ведь сначала идет i2cSlaveDownloadBuf она принимает данные и «кладет” их в переменную (i и z) далее выполняется программа - использует данные из переменных i и z, а потом они затираются i2cSlaveUploadBuf?
Ответить
0

[Автор]
Aleksey1408 #
Тк обработка I2c происходит в режиме прерываний, то после загрузки данных программа не ждет передачи данных, а сразу переходит следующим функциям, в присланном вам коде он заполняет массив по индексам 1 и 2, а начинается передача с 0. Учитывая что скорость по умолчанию100 кбит/с, а МК работает обычно на 8 МГц, то он модуль гарантированно не успевает передать данные, которые успешно затираются.
Ответить
0
tehcom11 #
Нулевой индекс занят другими делами.

А если после вызова функций i2cSlaveDownloadBuf запретить прерывания, код после них отработает, а перед i2cSlaveUploadBuf разрешить прерывания?
Ответить
0
tehcom11 #
Вызывая i2cSlaveUploadBuf я умышленно затираю данные в i2cSlaveDownloadBuf, но по задумке данные должны были отработать в в основном коде.
И не понятно как данные затираются раньше чем отрабатывают в коде, даже если i2c работает через прерывания, ведь вызов функции i2cSlaveUploadBuf происходит в конце основного кода?
Ответить
0

[Автор]
Aleksey1408 #
Прерывания происходят не в конце кода, а когда периферия выставить флаг прерывания. Модель работы Slave отличается от модели работы Master.
Master работает как символьное устройство, что является идеологически не правильным, задача I2C это синхронизация памяти, на потоковое устройство. Slave работает как общая память которая может считываться и записываться как мастером, так и slave. В этом смысле slave реализован более логично, чем Master.
Ответить
0
tehcom11 #
Чет все равно не понимаю. Я в слейве вызываю последовательно две функции чтения данных которые (данные) прислал мастер из ячеек 1 и 2 (нулевая ячейка обрабатывается отдельно и ее «стирать» не надо) и подразумеваю когда я вызываю функции тогда и начинается прерывание точнее прерывания происходят постоянно после инициализации шины, но обработка данных происходит только при вызове функции. А почему подготовка данных в слейве для чтения мастером (в моем случае эти данные (ячейка 1 и 2) используются как «стирание») происходит до вызова этих функций (i2cSlaveUploadBuf) как шина узнает что я собрался подготовить данные на отправку?
Ответить
0
tehcom11 #
«Учитывая что скорость по умолчанию100 кбит/с, а МК работает обычно на 8 МГц, то он модуль гарантированно не успевает передать данные, которые успешно затираются.» - правильно понимаю, код в слейве работает быстрее чем чем модуль i2c мастера успевает передать данные и тем самым получается что i2cSlaveUploadBuf затирает данные прежде чем данные в i2cSlaveDownloadBuf будут готовы для чтения?

Сделал следующий эксперимент, завел таймер счетчик и условие - таймер дошёл до 200 сработало условие -> вызвалась функция i2cSlaveUploadBuf, записала нули в 1 и 2 ячейки -> сбросился счетчик. Все хорошо, но только происходит задержка приема от мастера примерно на 200мсек. и основной код в это время не получает нужных данных от мастера. Как то эту задержку можно обойти?

А если переодически обнулять регистр TWDR?
Как я понял при потере слейвом связи с мастером этот регистр не обнуляется и функция i2cSlaveDownloadBuf постоянно считывает из него данные которые пришли перед разрывом?
Ответить
0

[Автор]
Aleksey1408 #
Я предлагаю вам, использовать ячейку памяти 3, в качестве флага успешного чтения мастером данных.
Т.е. Мастера в начале проверяет что данные в 3 ячейке равны 0, затем мастер прочитывает данные из 1 и 2 ячеек, тут же вы инициируете передачу данных на запись и записываете в ячейку 3 например 0xFF, а в это время Slave проверяет если появилась запись в 3 ячейки равная 0xFF, значит можно данные в буфер новые записывать, а флаг обнулять. В общем минипротокол должен получиться.
Ответить
0

[Автор]
Aleksey1408 #
Подскажите, а для какого камня на avr выпишите прошивку?
Ответить
0
tehcom11 #
Атмега16а
Отредактирован 20.03.2024 13:04
Ответить
0
tehcom11 #
Решил задачу следующим образом. Завел переменную extern, сбрасываю ее в прерывании i2c в "i2c.c", в main инкрементирую, в условии - если досчитала до 600 (экспериментальным путем выяснил что корректно работает от 400) значит от мастера ничего нет - прерывание не срабатывает, в условии вызываются i2cSlaveUploadBuf, система встает в ошибку.
Aleksey1408 Спасибо что откликнулись, очень помогли!
Ответить
0
tehcom11 #
@Aleksey1408 Приветствую.
Запускаю акселерометр ils3dh.
Есть регистры конфигурации, например возьмем регистр CTRL_REG1 (0x20) в нем 8мь бит конфигураций.
Как будет выглядеть запись в этот регистр?
Такая запись корректна?
i2cMasterUploadBuf(0x20); //адрес регистра
i2cMasterUploadBuf(0x6F); //настройки регистра 0x20
i2cMasterSendBuf(ADDRES_LIS3DH); //отправка настроек в датчик

И как будет выглядеть запись если я каждому биту в регистре конфигураций дам имя через макросы define?
Отредактирован 12.04.2024 01:07
Ответить
0

[Автор]
Aleksey1408 #
Можно конечно через define...
Но это очень не удобно. Лучше потратить чуть-чуть времени и сделать по-человечески через структуру тем самым потом будет очень удобно делать настройки и весь даташит будет всегда с собой. Кроме того, если где-то накосячили всегда можно быстро исправить. Плюсов много, недостаток один - надо потратить время на заполнение структуры. Я к сожалению не помню в AVR big-endian или little-endian. в зависимости от этого определяется порядок заполнения битовых полей. Я всегда делаю так.

typedef enum
{
powerDown=(uint8_t)0b0000,
hr_Norm_Low_1Hz=(uint8_t)1,
hr_Norm_Low_10Hz=(uint8_t)2,
hr_Norm_Low_25Hz=(uint8_t)3,
hr_Norm_Low_50Hz=(uint8_t)4,
hr_Norm_Low_100Hz=(uint8_t)5,
hr_Norm_Low_200Hz=(uint8_t)6,
hr_Norm_Low_400Hz=(uint8_t)7,
Low_1600Hz=(uint8_t)8,
hr_Norm_1344Hz_Low_5376Hz=9
} ODR_t;

typedef enum
{
normalMode=(uint8_t)0,
lowPowerMode=(uint8_t)1
} LPen_t;

typedef enum
{
disabel=(uint8_t)0,
enabel=(uint8_t)1
} en_t;


typedef union{
uint8_t CTRL_REG1; // доступ к целому регистру
struct // заполнение отдельных битов
{
ODR_t ODR : 4;
LPen_t LPen : 1;
en_t Zen:1;
en_t Yen:1;
en_t Xen:1;
};
} CTRL_REG1_t;

//------
int main(void)
{
//..............
CTRL_REG1_t reg1

reg1.ODR=hr_Norm_Low_50Hz;
reg1.LPen=normalMode;
reg1.Zen=enabel;
reg1.Yen=enabel;
reg1.Xen=disabel;
i2cMasterUploadBuf(0x20); //адрес регистра
i2cMasterUploadBuf(reg1.CTRL_REG1); //настройки регистра 0x20
i2cMasterSendBuf(ADDRES_LIS3DH); //отправка настроек в датчик
//...............
}
Прикрепленный файл: lis.c
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическое сопротивление?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

AVR-программатор USB ASP
AVR-программатор USB ASP
Набор для сборки - LED лампа Конструктор - темброблок на LM1036
вверх