» » » Несколько функций для програмной реализации протокола I2C на AVR

 
 
 
22

Несколько функций для програмной реализации протокола I2C на AVR

Разместил teXnik 7 февраля 2015. Просмотров: 9 729

Несколько функций для  програмной реализации протокола I2C на AVRДобрый день, дорогие друзья! Решил поделиться с вами несколькими функция для работы по протоколу I2C на микроконтроллерах AVR. Функции реализуют программную работу с протоколом в режиме Master.

Писал я прошивку для управления звуковым процессором. Писал в CodeVision и никаких проблем не было. Но пришлось пересесть на IAR, в котором не нашлось библиотек для программной реализации I2C.
Попытки запустить библиотеки, найденные на просторах Интернета, успеха не принесли и решил я написать необходимый минимум сам.

Протокол I2C

I2C — последовательная шина данных для связи интегральных схем, использующая две двунаправленные линии связи (SDA и SCL). Используется для соединения низкоскоростных периферийных компонентов с материнской платой, встраиваемыми системами и мобильными телефонами.
Для начала нам понадобиться много #define'ов

///i2c port 
#define i2c_time 10 // пауза в микросекундах (4 паузы на такт передачи)
#define i2c_port PORTB 	
#define i2c_ddr DDRB	
#define i2c_pin PINB
#define sda 2	       // номера выводов соответствующего порта
#define scl 3	
// управление битами при записи
#define clrsda i2c_port &= ~(1 < < sda)  
#define setsda i2c_port |= (1 < < sda)
#define clrscl i2c_port &= ~(1 < < scl)
#define setscl i2c_port |= (1 < < scl) 
// управление битами при чтении
#define rxscl i2c_ddr &= ~(1 < < scl) 
#define txscl i2c_ddr |= (1 < < scl) 
#define txsda i2c_ddr |= (1 < < sda)
#define rxsda i2c_ddr &= ~(1 < < sda) 

#define i2c_readstatus (1 < < sda)


Для работы потребуется функция осуществляющую задержку. Если вы используете IAR то её объявление выглядит так:

#ifndef DELAY_H
#define DELAY_H
#endif
#define CPU_CLK 8000000 //частота на которой работает контроллер
#define delay_us(u) __delay_cycles((CPU_CLK/1000000)*u)
#define delay_ms(m) __delay_cycles((CPU_CLK/1000)*m)


Функции

Передачу инициирует ведущее устройство, формируя состояние СТАРТ на шине. С этой функции я и начал.
void i2c_start(void)
{
asm("cli");
  txsda;       
  clrsda;
  delay_us(i2c_time);
  txscl;
  clrscl;
  delay_us(i2c_time);
asm("sei");
};

Функция ничего не возвращает, и на время своего выполнения запрещает все прерывания.
Окончание передачи также инициирует ведущее устройство формируя состояние СТОП на шине.

void i2c_stop(void)          
{
asm("cli");  
  txsda;
  clrsda;
  delay_us(i2c_time);
  rxscl;
  delay_us(i2c_time);
  rxsda;
asm("sei");
};


Передача одного байта данных:
char i2c_tx(char data)
{
asm("cli");
char x;
char b=1;
  for(x=8; x; x--)  // цикл на 8 передаваемых бит
  {
    if((data&0x80)==0)  // проверяем старший бит в передаваемом байте
    {
      txsda; // выдаем бит данных на SDA
      clrsda;
    }
    else
    {
      rxsda; // или оставляем линию в покое
    };
    txscl; // выдаем такт в линию SCL
    clrscl;
    delay_us(i2c_time);
    data < < = 1; // сдвигаем передаваемые биты влево
    rxscl;
    delay_us(i2c_time< < 1);
    txscl;
    clrscl;
    delay_us(i2c_time);
  };
  rxscl;
  delay_us(i2c_time);
  rxsda;
  if ((i2c_pin & i2c_readstatus)==i2c_readstatus)                      
  { 
    b=0;
  };    // считываем возможный ACK бит 
  delay_us(i2c_time); 
  txscl;
  clrscl;
  delay_us(i2c_time< < 1);
asm("sei");
  return b;
};

Функция возвращает 1, если slave подтвердил передачу и 0 если не подтвердил.

Чтение байта:
Передаем в качестве параметра ack единицу, если хотим, чтобы наш master дал подтверждение и любое другое число, чтобы ответить NACK.

char i2c_rx(char ack)
{
asm("cli");
char x; // счетчик
char data=0; // принимаемый байт 
  rxsda; // настраиваем sda на чтение
  for(x=0; x< 8; x++) // цикл приема бит
  {
    txscl;// выдаем такт на SCL
    clrscl;
    delay_us(i2c_time);
    rxscl;
    delay_us(i2c_time);
    if ((i2c_pin & i2c_readstatus)==i2c_readstatus) // считываем из порта              
    { 
      setb((7-x),data); // если считали единицу - устанавливаем соответствующий бит
    };
    delay_us(i2c_time);
    txscl;
    clrscl;
    delay_us(i2c_time);
  };
if(ack==1)  //выдаем или не выдаем ACK на шину
    {
      txsda;
      clrsda;
    }
    else
    {
      rxsda;
    };
    txscl;
    clrscl;
    delay_us(i2c_time);
    rxscl;
    delay_us(i2c_time< < 1);
    txscl;
    clrscl;
    delay_us(i2c_time);
asm("sei");
  return data;
};


Пример

Типичная передача данных по протоколу I2C выглядит следующим образом:
  1. СТАРТ
  2. Адрес+бит чтение/запись
  3. Чтение/Запись
  4. xxx
  5. Стоп


В качестве примера приведу функцию записи в аудио процессор tda7439.

void loadtoTDA(void)
{
char p;
i2c_start();
i2c_tx(0x88);// адрес тда
i2c_tx(0x10); // режим записи в тда - инкрементный
for (p=0;p< 8;p++)
    {
    i2c_tx(param[p]); // передаем массив с параметрами для тда
    };  
i2c_stop();    
};


Это минимум функций необходимый для работы мастера на шине. Все функции проверены и протестированы, как в симуляторе так и в "железе".
Надеюсь, они будут полезны не только мне, но и вам.
Спасибо за внимание!
Алексей (teXnik)
Россия, Тула
Профиль teXnik
Студент политехнического университета, увлекаюсь радиэлектроникой и модернизацией авто (подготовка для джип триала). Паять люблю. Переделал амфитоны 25 ас 27 и усилитель под них на тдашке.
Собрал уазик с мотором v8.

На сайт пришел для разватия своих навыков и умений
 

Понравилось? Палец вверх!

  • всего лайков: 58

Поделись с друзьями!

Связанные материалы:


Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 3. TFT дисплей 2.8" (240х320) на ILI9341... Битва за урожай закончена, можно продолжить повествование. Полноцветный TFT-дисплей 240×320...
Схема на Датагоре. Новая статья Программная реализация протокола SPI на AVR в CodeVisionAVR... Всем коллегам и согражданам привет! Увлёкся я изучением протоколов. Про реализацию протокола I2C...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 2. TFT дисплей 1.8" (128х160) на ST7735... Следующий из рассматриваемых нами модулей обладает полноцветным дисплеем под управлением...
Схема на Датагоре. Новая статья Программная реализация протокола I2C на AVR в CodeVisionAVR... Пару лет назад, изучая Atmega8, я захотел программно реализовать работу с устройствами на шине i2c,...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 1. OLED дисплей 0.96" (128х64) на SSD1306... Добрый день, друзья! Эта статья открывает цикл, посвящённый средствам визуального отображения...
Схема на Датагоре. Новая статья Беспроводной канал связи 2,4 ГГц на базе трансивера nRF24L01+ от Nordic Semiconductor. Часть 2... Продолжаем разговор. Как-то раз мы с пацанами решили соединить радио-модуль «nRF24L01+» вот с таким...
Схема на Датагоре. Новая статья Беспроводной канал связи 2,4 ГГц на базе трансивера nRF24L01+ от Nordic Semiconductor. Часть 1... Доброго вам дня, уважаемые граждане и гости Датагор.ру - этого замечательного сообщества...
Схема на Датагоре. Новая статья Таймер от 1 секунды до 100 часов на микроконтроллере ATMEL AT89C2051... Картинка от Novelty Kitchen Timers В радиолюбительской практике частенько необходим таймер:...
Схема на Датагоре. Новая статья Немного про шину 1-Wire и цифровой термометр DS18b20... Фотка от www.150cc.ru Здравствуйте, друзья. Хочу предложить вашему вниманию несколько простых...
Схема на Датагоре. Новая статья Arduino shield: акселерометр на LIS302DL... Собрал недавно arduino на atmega8, поморгал диодом, захотелось большего. Начал изучать различные...
Схема на Датагоре. Новая статья Микроконтроллеры. Связь с внешним миром. Часть 2... Здравствуйте, дорогие Датагорцы! По некоторым слабо зависящим от меня причинам не мог участвовать в...
Схема на Датагоре. Новая статья Грызем микроконтроллеры. Урок 2.... Предлагаю продолжить изучение микроконтроллеров… Второй урок будет посвящен по большей части...
<
  • Гражданин
7 февраля 2015 02:51

Владимир / vladimirm2

Цитата
  • С нами с 5.01.2010
  • Ушёл в реал Пользователь offline
  • 131 комментарий
  • 14 публикаций
 
  • 0
Хорошая статья, спасибо!
Скажите пожалуйста, Алексей, Вы не пробовали писать скейчи для ардуино? Я очень далек от программирования и ищу кто бы мог помочь написать скейч для управления аудиопроцессором TDA7318 по I2C. Управление с пульта с выводом информации на какой либо экран ( OLED LCD Display I2C 0.96 IIC Serial 128X64 ?
Впрочем вопрос ко всем, может кто поможет?

<
  • Гражданин
7 февраля 2015 04:21

Алексей / teXnik

Цитата
  • С нами с 1.11.2009
  • Ушёл в реал Пользователь offline
  • 33 комментария
  • 5 публикаций
 
  • +1
Нет, не пробовал. Вообще ни разу не держал ардуину в руках. С ардуино помочь не смогу

<
  • Кандидат
7 февраля 2015 22:32

Евгений / demiurg1978

Цитата
  • С нами с 16.02.2014
  • Ушёл в реал Пользователь offline
  • 5 комментариев
  • 0 публикаций
 
  • 0
У автора вижу строки asm("cli"); asm("sei");
В IAR есть свои функции разрешения, запрета прерываний.
#include <intrinsics.h>
__enable_interrupt;
__disable_interrupt;

Кстати, пошукайте в этом файле, там еще полезные вещи есть.

<
  • Гражданин
8 февраля 2015 03:00

Алексей / teXnik

Цитата
  • С нами с 1.11.2009
  • Ушёл в реал Пользователь offline
  • 33 комментария
  • 5 публикаций
 
  • 0
Цитата: demiurg1978
...В IAR есть свои функции разрешения, запрета прерываний...

Спасибо, не знал!

<
  • Прохожий
8 февраля 2015 09:40

/ andrdoy

Цитата
  • С нами с --
  • 0 комментариев
  • 0 публикаций
 
  • +1
Все бы хорошо, но задержка задаваемая в программе, ни как не звязана с частотой процессора, то есть частота шины при разных кварцах, будет разная.

Так же полное отсутствие контроля ошибок на шине, в идеале все конечно будет работать, но как только одна их подчененных микросхем И2Ц начнет выделываться, результат не предсказуем.

Для тестов нормально, а для серьезного дела я советую использовать библиотеку отсюда:
=//homepage.hispeed.ch/peterfleury/avr-software.html
Она проверена на практике и лишена перечисленных недостатков.

Отредактировал и дополнил Датагор 08/02

<
  • Гражданин
8 февраля 2015 20:58

Сергей / Sergiy_83

Цитата
  • С нами с 16.10.2012
  • Ушёл в реал Пользователь offline
  • 35 комментариев
  • 4 публикации
 
  • 0
Цитата: teXnik
Писал в CodeVision и никаких проблем не было. Но пришлось пересесть на IAR, в котором не нашлось библиотек для программной реализации I2C.

Какие проблемы заставили пересесть на IAR?

<
  • Гражданин
8 февраля 2015 21:39

Алексей / teXnik

Цитата
  • С нами с 1.11.2009
  • Ушёл в реал Пользователь offline
  • 33 комментария
  • 5 публикаций
 
  • 0
Цитата: Sergiy_83
Какие проблемы заставили пересесть на IAR?
1. Писал программный спектроанализатор и он после компиляции не влез в контроллер (после правда переделал на аппаратный но так и оставил проект в IAR'е)
2. Протеус умеет построчно выполнять код из IAR - удобно при отладке. (Возможно кодвижион тоже умеет компилирировать в формат *.D90)

Цитата: andrdoy
...частота шины при разных кварцах, будет разная
#define CPU_CLK 8000000 //частота на которой работает контроллер: меняем кварц - меняем и частоту и все будет работать на той же скорости.
Что касается контроля исключительных ситуаций - ни кто не мешает его дописать; я же привел необходимый минимум.

<
  • Кандидат
8 февраля 2015 21:49

Евгений / demiurg1978

Цитата
  • С нами с 16.02.2014
  • Ушёл в реал Пользователь offline
  • 5 комментариев
  • 0 публикаций
 
  • 0
IAR умеет делать формат для AVR-Studio. И проекты сделанные на IAR можно гонять в симуляторе AVR-Studio.
Хоть сишный код, хоть дизасм.
По моему скромному мнению, Протеус - программный симулятор и он многое не покажет.
Настоящая отладка только на железе!

<
  • Гражданин
8 февраля 2015 22:49

Алексей / teXnik

Цитата
  • С нами с 1.11.2009
  • Ушёл в реал Пользователь offline
  • 33 комментария
  • 5 публикаций
 
  • 0
Согласен. пишешь прошивку отлаживаешь в протеусе, травишь... паяешь... заливаешь... ругаешься... переписывешь прошивку... переделываешь печатку... Так у меня было)

<
  • Кандидат
14 февраля 2015 09:09

Михаил / mk85

Цитата
  • С нами с 29.10.2012
  • Ушёл в реал Пользователь offline
  • 1 комментарий
  • 0 публикаций
 
  • 0
В медленных и простых устройствах программная реализация протокола I2C несомненно работает и позволяет хорошо изучить этот протокол на первых этапах. Но при управлении несколькими микросхемами по I2C да ещё с критичной ко времени исполнения программе, непременно возникнут конфликты. Особенно напрягают for и while при работе с медленной периферией. В мк AVR eсть аппаратный I2C со всеми необходимыми настройками и, самое главное, прерыванием. Программная поддержка описана в аппноуте AVR315, который можно подстроить под себя. Кстати, WinAvr практически тот же IAR, но бесплатный, встроен в AStudio6, создаёт файл .elf для отладки в Proteus.

<
  • Гражданин
15 февраля 2015 14:05

Игорь Рогов / AudioKiller

Цитата
  • С нами с 10.01.2012
  • Ушёл в реал Пользователь offline
  • 293 комментария
  • 3 публикации
 
  • +1
В функции передачи байта есть строка:

for(x=8; x; x--) // цикл на 8 передаваемых бит

Что-то в ней не то...

<
  • Главный редактор
15 февраля 2015 14:28

Игорь Петрович Котов / Datagor

Цитата
  • С нами с 25.02.2011
  • Ушёл в реал Пользователь offline
  • 1 648 комментариев
  • 261 публикация
 
  • 0
Наверное, так
for (x=8; x>0; x--)
или так
for (x=8; x=1; x--)

В обоих случаях Х декрементируется от 8 до 1 за 8 итераций и цикл завершает работу.

Надеюсь, Алексей / teXnik прокомментирует, может я чего не вижу.

<
  • Гражданин
15 февраля 2015 14:45

Сергей / Sergiy_83

Цитата
  • С нами с 16.10.2012
  • Ушёл в реал Пользователь offline
  • 35 комментариев
  • 4 публикации
 
  • 0
Цитата: AudioKiller
В функции передачи байта есть строка:
Что-то в ней не то...

В ней непривычная форма записи условия. В данном случае компилятор понимает как x!=0 или x>0

<
  • Гражданин
15 февраля 2015 15:06

Алексей / teXnik

Цитата
  • С нами с 1.11.2009
  • Ушёл в реал Пользователь offline
  • 33 комментария
  • 5 публикаций
 
  • 0
Цитата: AudioKiller
В функции передачи байта есть строка:

for(x=8; x; x--) // цикл на 8 передаваемых бит

Что-то в ней не то...

Как уже сказал Сергей / Sergiy_83, это просто не совсем обычна форма записи и компилятор действительно воспринимает как x!=0 или x>0. Собственно, если X дополнить условием !=0 или >0 ничего не измениться и цикл по прежнему будет выполняться 8 раз по числу передаваемых бит.

<
  • Гражданин
17 февраля 2015 03:44

Игорь Рогов / AudioKiller

Цитата
  • С нами с 10.01.2012
  • Ушёл в реал Пользователь offline
  • 293 комментария
  • 3 публикации
 
  • 0
Да, по идее если х!=0, то Си воспринимает его как логическую единицу и продолжает цикл. Просто а) выглядит как опечатка; б) все же лучше использовать явное указание конца цикла, т.к. и понятнее (программа лучше документирована) и если вдруг получится, х<0 (мало ли какой косяк), то программа будет работать неправильно.

<
  • Кандидат
18 июня 2015 23:01

Валерий / 12val12

Цитата
  • С нами с 18.06.2015
  • Ушёл в реал Пользователь offline
  • 1 комментарий
  • 0 публикаций
 
  • 0
Здравствуйте, Алексей! А можно увидеть вашу такую же программу на CodeVision?
Мне надо поуправлять видеопроцом TB1245.

Кстати, функцию i2c_stop не закрыли } на листинге.

<
  • Главный редактор
19 июня 2015 03:43

Игорь Петрович Котов / Datagor

Цитата
  • С нами с 25.02.2011
  • Ушёл в реал Пользователь offline
  • 1 648 комментариев
  • 261 публикация
 
  • 0
Цитата: 12val12
i2c_stop не закрыли } на листинге
Валерий, спасибо, поправили.

<
  • Прохожий
8 июля 2016 19:24

/ xorkrus

Цитата
  • С нами с --
  • 0 комментариев
  • 0 публикаций
 
  • 0
А что за
setb((7-x),data); // если считали единицу - устанавливаем соответствующий бит

этот setb больше нигде не появляется.

<
  • Прохожий
11 июля 2016 12:40

/ xorkrus

Цитата
  • С нами с --
  • 0 комментариев
  • 0 публикаций
 
  • 0
//pastebin.com/XWM3Q45H мой код
Проконсультируйте пожалуйста, всё ли верно?
bcd.h это преобразование 2-10-2.
В итоге - не работает. Не могу понять моя ошибка с регистрами или что-то не так делаю?

<
  • Кандидат
21 августа 2016 19:14

Павел Сумароков / Electronik83

Цитата
  • С нами с 29.08.2015
  • Ушёл в реал Пользователь offline
  • 4 комментария
  • 2 публикации
 
  • 0
Зачем каждый раз на пин порта выводить 0?
Один раз в начале на оба пина подать 0 и все. Рулить тока направлением DDR пина.
Переписываю данный код под CodeVisionAVR.

<
  • Кандидат
22 августа 2016 02:30

Павел Сумароков / Electronik83

Цитата
  • С нами с 29.08.2015
  • Ушёл в реал Пользователь offline
  • 4 комментария
  • 2 публикации
 
  • 0
Если кому интересно, есть вариант без подтягивающих резисторов...

Добился нормальной осциллограммы. Если кому интересно - будет код.

<
  • Главный редактор
24 августа 2016 13:55

Игорь Петрович Котов / Datagor

Цитата
  • С нами с 25.02.2011
  • Ушёл в реал Пользователь offline
  • 1 648 комментариев
  • 261 публикация
 
  • 0
Павел, спасибо!
У нас всё проще: выверенный код оформляйте в виде статьи, она попадёт в раздел "Библиотеки кодов".
Сможете - дополняйте примерами использования, фотками, видео и т.п.

О написании статей тут: //datagor.ru/blogs/blog_of_datagor/256-how2write.html
Видосы и файлы сюда: //datagor.ru/blogs/datagor/2904-datagorskiy-ftp-server-dlya-videorolikov.html
Вопросы мне в личку, в аську, в скайп.

Добавление комментария


Налетай! Паяльники, станции, жала с доставкой
  • smilelolbyewinkyahoocoollaughing
    crazybadcryingsadirefulsickstraight
    ballooncakegooddrinksmailbombsun
    nightrainstarscolddashguitar-manhandshake
    musicnegativenopardonshoksleepunknown
    wackoyawnblushbullyhashsmokingwhew
Скопируйте текст вашего комментария на случай неверного ответа на контрольный вопрос.