В данном уроке мы попробуем с помощью библиотеки LL поработать с шиной I2C контроллера STM32F4. В качестве подопытного устройства мы возьмём микросхему EEPROM — AT24C32, которая установлена в модуле с часовой микросхемой DS3231 и также в часовом модуле с микросхемой DS1307. С данной микросхемой мы уже неоднократно работали, в том числе и с контроллером STM32, причём с применением библиотеки LL также работали, только с серией STM32F1. Причём микросхему мы использовали с часовым модулем на микросхеме DS1307. Данный модуль мы будем использовать и сегодня.
Шина I2C у контроллера линейки STM32F4 практически ничем не отличается от той же шины контроллера STM32F1, поэтому изучать нам аппаратную составляющую не придётся и даже в инициализации шины происходят те же действия. Единственное отличие — появление в линейке STM32F4 аналогового и цифрового фильтров, вследствие чего здесь также появился следующий новый регистр — регистр управления фильтрами
Рассмотрим назначение битов данного регистра.
ANOFF (Analog noise filter OFF)
0 — аналоговый фильтр включен,
1 — аналоговый фильтр отключен.
DNF[3:0] (Digital noise filter)
0000 — цифровой шумовой фильтр отключен
0001 — цифровой шумовой фильтр включен и возможность его фильтрации — 1 * tPCLK1
…
1111 — цифровой шумовой фильтр включен и возможность его фильтрации — 15 * tPCLK1
В остальном особых отличий не замечено, поэтому достигнуть цель нашего урока — записать во внешнюю память информацию, а затем считать её — будет несложно. Также данную информацию мы отобразим затем на нашем цветном дисплее, присутствующем в отладочной плате, с которой мы в данном уроке по-прежнему работаем — STM32F429I-Discovery.
Подключим к нашей плате модуль с микросхемой к соответствующим ножкам. Питание можно брать как 3,3 вольта, так и 5. Я взял 5 вольт, так как контакт находится ближе. Общий провод платы также соединяем с общим проводом модуля, а ножки для SDA и SCL используем соответственно PB7 и PB6, не забывая в дальнейшем о том, что в Cube MX мы должны будем задействовать также именно эти ножки.
После этого наша схема будет выглядеть так
Проект за основу мы возьмём из урока 209 с именем LL_SPI_ILI9341_DMA и присвоим ему имя LL_I2C_EEPROM.
Откроем наш проект в Cube MX и включим I2C1, оставив все настройки в нём по умолчанию
Мы видим, что именно нужные нам ножки задействованы для шины
В менеджере задействуем на I2C библиотеку LL
Сгенерируем проект и откроем его в Cube IDE.
Перейдём в файл main.c и объявим два глобальных массива. Один из них будет для хранения считанных из внешнего EEPROM значений, а другой будет состоять из заранее заданных 8-битных чисел для записи в микросхему
1 2 3 4 5 6 |
uint32_t dma_spi_cnt=1; uint8_t rd_value[20] = {0}; uint8_t wr_value[20] = {0x14,0x13,0x12,0x11,0x10, 0x0F,0x0E,0x0D,0x0C,0x0B, 0x0A,0x09,0x08,0x07,0x06, 0x05,0x04,0x03,0x02,0x01}; |
Добавим также некоторые макросы для работы с шиной, а именно адрес SLAVE микросхемы и переменные для инициализации бита записи/чтения в адресе
1 2 3 4 |
#define LED2_OFF() LL_GPIO_ResetOutputPin(GPIOG, LL_GPIO_PIN_14) #define I2C_REQUEST_WRITE 0x00 #define I2C_REQUEST_READ 0x01 #define SLAVE_OWN_ADDRESS 0xA0 |
Нам нужно будет также написать две функции для работы с внешней энергонезависимой памятью подобно тому, как мы делали в уроке 150 для контроллера STM32F1.
К счастью, писать нам их не придётся, так как со времён 150 урока они совершенно не изменились. Вот их код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
//------------------------------------------------------- void AT24C_WriteBytes (uint16_t addr,uint8_t *buf, uint16_t bytes_count) { uint16_t i; //Disable Pos LL_I2C_DisableBitPOS(I2C1); LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK); LL_I2C_GenerateStartCondition(I2C1); while(!LL_I2C_IsActiveFlag_SB(I2C1)){}; //read state (void) I2C1->SR1; LL_I2C_TransmitData8(I2C1, SLAVE_OWN_ADDRESS | I2C_REQUEST_WRITE); while(!LL_I2C_IsActiveFlag_ADDR(I2C1)){}; LL_I2C_ClearFlag_ADDR(I2C1); LL_I2C_TransmitData8(I2C1, (uint8_t) (addr>>8)); while(!LL_I2C_IsActiveFlag_TXE(I2C1)){}; LL_I2C_TransmitData8(I2C1, (uint8_t) addr); while(!LL_I2C_IsActiveFlag_TXE(I2C1)){}; for(i=0;i<bytes_count;i++) { LL_I2C_TransmitData8(I2C1, buf[i]); while(!LL_I2C_IsActiveFlag_TXE(I2C1)){}; } LL_I2C_GenerateStopCondition(I2C1); } //------------------------------------------------------- void AT24C_ReadBytes (uint16_t addr, uint8_t *buf, uint16_t bytes_count) { uint16_t i; //Disable Pos LL_I2C_DisableBitPOS(I2C1); LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK); LL_I2C_GenerateStartCondition(I2C1); while(!LL_I2C_IsActiveFlag_SB(I2C1)){}; //read state (void) I2C1->SR1; LL_I2C_TransmitData8(I2C1, SLAVE_OWN_ADDRESS | I2C_REQUEST_WRITE); while(!LL_I2C_IsActiveFlag_ADDR(I2C1)){}; LL_I2C_ClearFlag_ADDR(I2C1); LL_I2C_TransmitData8(I2C1, (uint8_t) (addr>>8)); while(!LL_I2C_IsActiveFlag_TXE(I2C1)){}; LL_I2C_TransmitData8(I2C1, (uint8_t) addr); while(!LL_I2C_IsActiveFlag_TXE(I2C1)){}; LL_I2C_GenerateStartCondition(I2C1); while(!LL_I2C_IsActiveFlag_SB(I2C1)){}; (void) I2C1->SR1; LL_I2C_TransmitData8(I2C1, SLAVE_OWN_ADDRESS | I2C_REQUEST_READ); while (!LL_I2C_IsActiveFlag_ADDR(I2C1)){}; LL_I2C_ClearFlag_ADDR(I2C1); for(i=0;i<bytes_count;i++) { if(i<(bytes_count-1)) { while(!LL_I2C_IsActiveFlag_RXNE(I2C1)){}; buf[i] = LL_I2C_ReceiveData8(I2C1); } else { LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_NACK); LL_I2C_GenerateStopCondition(I2C1); while(!LL_I2C_IsActiveFlag_RXNE(I2C1)){}; buf[i] = LL_I2C_ReceiveData8(I2C1); } } } //------------------------------------------------------- |
Перейдём в функцию main(), где сначала из бесконечного цикла удалим весь пользовательский код.
Затем объявим локальный символьный массив
1 2 |
uint16_t i,j; char str1[20] = {}; |
Настроим цвет и размер шрифта, а также цвет фона
1 2 3 4 |
TFT9341_FillScreen(TFT9341_BLACK); TFT9341_SetTextColor(TFT9341_YELLOW); TFT9341_SetBackColor(TFT9341_BLACK); TFT9341_SetFont(&Font24); |
Попытаемся записать данные в EEPROM с какого-нибудь адреса
1 2 |
TFT9341_SetFont(&Font24); AT24C_WriteBytes (0x024A, wr_value, 20); |
Затем просто зажжём светодиод, чтобы убедиться, что мы не повисли и дошли до этого места
1 2 |
AT24C_WriteBytes (0x024A, wr_value, 20); LED1_ON(); |
Конечно, это не даёт нам право точно быть уверенными, что все данные передались и записались в память, полную картину мы получим, когда их прочитаем оттуда. Соберём код, прошьём контроллер и увидим, что светодиод наш светится
Теперь попробуем данные с того же адреса прочитать.
Закомментируем пока вызов функции записи
//AT24C_WriteBytes (0x024A, wr_value, 20);
Прочитаем наши данные в буфер чтения и отобразим их на дисплее
1 2 3 4 5 6 7 8 9 10 11 |
//AT24C_WriteBytes (0x024A, wr_value, 20); AT24C_ReadBytes (0x024A, rd_value, 20); for(j=0;j<5;j++) { str1[0] = 0; for (i=0;i<4;i++) { sprintf(str1 + i*3,"%02X ", rd_value[j*4+i]); } TFT9341_String(20,20+j*30,str1); } |
Соберём код, прошьём контроллер и посмотрим результат чтения на дисплее
Данные считаны верно и отображены.
Таким образом, на данном уроке мы научились работать с шиной I2C контроллера STM32F4 с использованием возможности библиотеки LL, что позволило нам записать и считать данные по данной шине в микросхему памяти EEPROM AT24C32. Конечно, научились — это громко сказано, можно сказать повторили работу с шиной I2C. Также нам удалось прочитанные данные отобразить на цветном дисплее.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь STM32F429I-DISCO
Модуль RTC DS3231 с микросхемой памяти
Смотреть ВИДЕОУРОК (нажмите на картинку)
что то я регистра фильтров не вижу?