Писал я прошивку для управления звуковым процессором. Писал в CodeVision и никаких проблем не было. Но пришлось пересесть на IAR, в котором не нашлось библиотек для программной реализации I2C.
Попытки запустить библиотеки, найденные на просторах Интернета, успеха не принесли и решил я написать необходимый минимум сам.
Содержание статьи / Table Of Contents
↑ Протокол 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 выглядит следующим образом:- СТАРТ
- Адрес+бит чтение/запись
- Чтение/Запись
- xxx
- Стоп
В качестве примера приведу функцию записи в аудио процессор 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();
};
Это минимум функций необходимый для работы мастера на шине. Все функции проверены и протестированы, как в симуляторе так и в "железе".
Надеюсь, они будут полезны не только мне, но и вам.
Спасибо за внимание!
Камрад, рассмотри датагорские рекомендации
🌼 Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.