Урок 16
Часть 5
Интерфейс TWI (I2C)
В предыдущей части занятия уже начали работать с кодом всерьёз и передали адрес устройства и адрес первой ячейки памяти в шины TWI.
Ну, давайте продолжим начатое дело.
Дальше мы работаем в соответствии с той диаграммой из даташита микросхемы, которую мы посмотрели в предыдущей части урока.
Теперь мы уже начинаем непосредственно тем же самым образом передавать байты для записи в ячейки памяти. Адрес ячейки после записи каждого байта будет автоматически прибавляться.
Передать мы попробуем сразу 32 байта. А чтобы не было слишком много кода, мы создадим массив в файле DS1307Eeprom.c
#include «main.h»
unsigned char bt[32];
Затем запишем в каждую ячейку массива число в функции main() после передачи адреса ячейки памяти, ну можно, конечно и раньше, но мы проинициализируем массив тут
I2C_SendByte(0);//передаем младшую часть адреса ячейки памяти
USART_Transmit(TWSR);//читаем статусный регистр
bt[0]=0x30; bt[1]=0x31; bt[2]=0x32; bt[3]=0x33; bt[4]=0x34; bt[5]=0x35; bt[6]=0x36; bt[7]=0x37;
bt[8]=0x38; bt[9]=0x39; bt[10]=0x3A; bt[11]=0x3B; bt[12]=0x3C; bt[14]=0x3D; bt[14]=0x3E; bt[15]=0x3F;
bt[16]=0x40; bt[17]=0x41; bt[18]=0x42; bt[19]=0x43; bt[20]=0x44; bt[21]=0x45; bt[22]=0x46; bt[23]=0x47;
bt[24]=0x48; bt[25]=0x49; bt[26]=0x4A; bt[27]=0x4B; bt[28]=0x4C; bt[29]=0x4D; bt[30]=0x4E; bt[31]=0x4F;
Теперь непосредственно начнём передачу в шину, а, следовательно, и в ячейки памяти микросхемы.
Так как алгоритм передачи байта одинаков, то мы вполне можем использовать цикл for. Создадим локальную переменную в main()
int main(void)
{
int i=0;
А теперь, прежде чем написать цикл, мы напишем особую функцию для передачи. Там уже будет отслеживаться статус операции (не всё же нам время в терминал смотреть), в результате которого мы будем. если что-то не так, генерировать ошибку.
А так как мы будем проверять разные статусы, то давайте в файл eepromext.h напишем некоторые макросы для вариантов статусов
#include «main.h»
#define TW_MT_DATA_ASK 0x28 // Ведущий передал данные и ведомый подтвердил прием
#define TW_MR_DATA_ASK 0x50 // Ведущий принял данные и передал подтверждение
#define TW_MR_DATA_NASK 0x58 // Ведущий передал данные и ведомый подтвердил прием
Функцию напишем в файле eepromext.c
#include «eepromext.h»
int EE_WriteByte(unsigned char c)
{
TWDR=c;//запишем байт в регистр данных
TWCR = (1<<TWINT)|(1<<TWEN);//включим передачу данных
while(!(TWCR&(1<<TWINT)));//подождем пока установится TWIN
if ((TWSR & 0xF8) != TW_MT_DATA_ASK)
{
return 1;
}
return 0;
}
В принципе мы здесь делаем то же самое, что и в функции I2C_SendByte, только мы проверяем статус. Если будет статус не тот, то функция вернёт 1, а если всё нормально — то 0.
Также мы видим здесь то, что здесь применен сброс в ноль младших трех битов. Хотя в данной ситуации это не имеет смысла, так как они и так в нуле, мы не используем биты деления, но для универсальности пусть будет маскирование, так как мало ли, когда-то придется использовать делитель.
Теперь давайте данную функцию и будем использовать в цикле for в main(), только проверять результат мы не будем. Если что-то пойдёт не так, то тогда и проверим. Пока будем читать статусный регистр обычным способом — через USART
bt[24]=0x48; bt[25]=0x49; bt[26]=0x4A; bt[27]=0x4B; bt[28]=0x4C; bt[29]=0x4D; bt[30]=0x4E; bt[31]=0x4F;
for(i=0;i<=31;i++)
{
EE_WriteByte(bt[i]);
USART_Transmit(TWSR);//читаем статусный регистр
}
I2C_StopCondition(); //Отправим условие STOP
USART_Transmit(TWSR);//читаем статусный регистр
Теперь давайте скомпилируем проект, прошьём контроллер открыв терминальную прогармму и запустив в ней соединение.
Мы должны увидиеть вот такой результат
То есть ведущий у нас принял все байты, включая и свой адрес устройства.
Конечно, пока мы не умеем читать данные из шины, то мы, соответственно, не сможем и проверить, нормально ли записались данные в память EEPROM.
Этим делом мы уже займёмся с помощью специальной функции чтения на следующем занятии. А в чтении есть свои подводные камни.
Предыдущая часть Программирование МК AVR Следующая часть
Техническая документация на микросхему AT24C32
Программатор и модуль RTC DS1307 с микросхемой памяти можно приобрести здесь:
Программатор USBASP USBISP с адаптером USBASP USBISP 3.3 с адаптером
Модуль RTC DS1307 с микросхемой памяти
Смотреть ВИДЕОУРОК (нажмите на картинку)
можно было инициализировать массив таким способом:
bt[32] = {0};
uin8_t i;
for(i=0; i<32; i++)
{
bt[i] = 0x30+i;
}
Можно было его вообще не создавать.
[#define TW_MR_DATA_NASK 0x58 // Ведущий передал данные и ведомый подтвердил прием]
Может всёж таки НЕ подтвердил приём? На что намекает «_NASK»…
Кст, эти дефайны уже определены в «util\twi.h» (AVR GCC toolchain).