STM Урок 9. HAL. Шина I2C. Продолжаем работу с DS3231



 

Урок 9

HAL. Шина I2C. Продолжаем работу с DS3231

 

На прошлом занятии мы как следует ознакомились с шиной I2C, а также с микросхемой часов реального времени DS3231, создали и настроили проект в Cube MX и Keil.

На данном занятии мы продолжим работать с тем же проектом и уже займёмся непосредственно кодом.

В файл main.h мы также добавим переменную для данных

#include «RTC.h»

 

//——————————————

uint8_t aTxBuffer[8];

 

В неё мы будем принимать значения сразу всех 7 регистров микросхемы и, предварительно заполнив в ней все ячейки, туда же и отправлять, когда будем устанавливать время в часах.

Екстерналим ее в i2c.c, также напишем там переменную для строки и напишем там две функции для работы с шиной.

 

#include «i2c.h»

//——————————————

extern uint8_t aTxBuffer[8];

//——————————————

char str[100];

//——————————————

void I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf)

{

        while(HAL_I2C_Master_Transmit(&hi, (uint16_t)DEV_ADDR,(uint8_t*) &aTxBuffer, (uint16_t)sizebuf, (uint32_t)1000)!= HAL_OK)

        {

                        if (HAL_I2C_GetError(&hi) != HAL_I2C_ERROR_AF)

                        {

                                sprintf(str, «Buffer error»);

                                LCD_SetPos(8, 0);

                                LCD_String(str);

                        }

        }

}

//——————————————

void I2C_ReadBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf)

{

        while(HAL_I2C_Master_Receive(&hi, (uint16_t)DEV_ADDR, (uint8_t*) &aTxBuffer, (uint16_t)sizebuf, (uint32_t)1000)!= HAL_OK)

        {

                        if (HAL_I2C_GetError(&hi) != HAL_I2C_ERROR_AF)

                        {

                                sprintf(str, «Buffer error»);

                                LCD_SetPos(8, 0);

                                LCD_String(str);

                        }

        }

}

 

Первая функция будет для передачи данных в шину, а вторая — для приема.

Входные параметры в первой функции:

I2C_HandleTypeDef hi — идентификатор шины I2C.

DEV_ADDR — адрес устройства

sizebuf — количество байт, которые мы будем передавать.

Дальше идёт цикл, из которого мы выйдем тогда, когда данные передадутся и мы получим статус HAL_OK.

Статус нам уже будет возвращать стандартная функция передачи данных в I2C из библиотеки HAL HAL_I2C_Master_Transmit. В данную функцию мы передаём практически те же параметры, некоторые из них только явно преобразованы несколько в другой тип, а также передаём таймаут в милисекундах. Это не значит, что функция будет именно столько времени выполняться. При успешном выполнении мы будем из функции возвращаться мгновенно. А вот если что-то пойдёт не так, то будем ждать именно столько времени, а затем всё равно выйдем, правда с ошибкой.

Затем мы проверяем передачу на ошибку, и в случае, если она будет, то выведем текст ошибки на дисплей.

Во второй функции абсолютно такие же входные параметры, только там мы уже будем читать байты и отправлять их в тот же буфер.

Тело функции также аналогично телу предыдущей функции, только библиотечная фунция там уже применена HAL_I2C_Master_Receive.

Создадим для этих двух функций прототипы в файле i2c.h

 

#include «lcd.h»

void I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf);
void I2C_ReadBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf);

 

 

Убираем весь код по дисплею кроме инициализации из главной функции.

Начинаем работать с шиной в главной функции main()

Сначала удалим код вывода тестовых строк на экран дисплея. Оставим только вот это

 

  /* USER CODE BEGIN 2 */
 LCD_ini();
 LCD_Clear();

  /* USER CODE END 2 */

 

Дальше уже начнем работать с функциями.

Пишем код в бесконечный цикл

 

  while (1)

  {

                LCD_SetPos(0,1);

                aTxBuffer[0]=0;

                I2C_WriteBuffer(hi2c1,(uint16_t)0xD0,1);

                while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)

                {

                }

 

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

Далее мы передадим адрес устройства и адрес первого регистра микросхемы.

Пишем дальше в бесконечный цикл

 

                while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)

                {

                }

                I2C_ReadBuffer(hi2c1,(uint16_t)0xD0,7);

 

А затем мы уже считаем все 7 регистров в буфер из RTC

 

 

Так как данные мы приняли в двоично-десятичном коде, то мы не можем их нормально показать без преобразования. Поэтому напишем две функции преобразования в файле RTC.c

 

#include «RTC.h»

 

uint8_t RTC_ConvertFromDec(uint8_t c)

{

  uint8_t ch = ((c>>4)*10+(0x0F&c));

        return ch;

}

 

uint8_t RTC_ConvertFromBinDec(uint8_t c)

{

        uint8_t ch = ((c/10)<<4)|(c%10);

        return ch;

}

 

Также пропишем им прототипы в одноименном хедере.

 

#ifndef RTC_H_

#define RTC_H_

 

#include «stm32f4xx_hal.h»

 

uint8_t RTC_ConvertFromDec(uint8_t c); //перевод двоично-десятичного числа в десятичное

uint8_t RTC_ConvertFromBinDec(uint8_t c); //перевод десятичного числа в двоично-десятичное

 

#endif /* RTC_H_ */

 

Добавим переменные

 

  /* USER CODE BEGIN 1 */

        uint32_t i=0;

        uint8_t sec=0,min=0,hour=0,day=0,date=0,month=0,year=0;

  /* USER CODE END 1 */

 

Дальше рутина.

 

                I2C_ReadBuffer(hi2c1,(uint16_t)0xD0,7);

                date=aTxBuffer[4];

                date = RTC_ConvertFromDec(date); //Преобразуем в десятичный формат

                LCD_SendChar((char) ((date/10)%10) + 0x30);

                LCD_SendChar((char) (date%10) + 0x30);

                LCD_SendChar(':');

                month=aTxBuffer[5];

                month = RTC_ConvertFromDec(month); //Преобразуем в десятичный формат

                LCD_SendChar((char) ((month/10)%10) + 0x30);

                LCD_SendChar((char) (month%10) + 0x30);

                LCD_SendChar(':');

                year=aTxBuffer[6];

                year = RTC_ConvertFromDec(year); //Преобразуем в десятичный формат

                LCD_SendChar((char) ((year/10)%10) + 0x30);

                LCD_SendChar((char) (year%10) + 0x30);

                LCD_SendChar(':');

                day=aTxBuffer[3];

                day = RTC_ConvertFromDec(day); //Преобразуем в десятичный формат

                LCD_SendChar((char) (day%10) + 0x30);

                LCD_SendChar(':');

                hour=aTxBuffer[2];

                hour = RTC_ConvertFromDec(hour); //Преобразуем в десятичный формат

                LCD_SendChar((char) ((hour/10)%10) + 0x30);

                LCD_SendChar((char) (hour%10) + 0x30);

                LCD_SendChar(':');

                min=aTxBuffer[1];

                min = RTC_ConvertFromDec(min); //Преобразуем в десятичный формат

                LCD_SendChar((char) ((min/10)%10) + 0x30);

                LCD_SendChar((char) (min%10) + 0x30);

                LCD_SendChar(':');

                sec=aTxBuffer[0];

                sec = RTC_ConvertFromDec(sec); //Преобразуем в десятичный формат

                LCD_SendChar((char) ((sec/10)%10) + 0x30);

                LCD_SendChar((char) (sec%10) + 0x30);

 

                LCD_SetPos(0,0);

                LCD_SendChar((char) ((i/100)%10) + 0x30);

                LCD_SendChar((char) ((i/10)%10) + 0x30);

                LCD_SendChar((char) (i%10) + 0x30);

                LCD_SetPos(6,2);

                LCD_SendChar((char) (((i+500)/100)%10) + 0x30);

                LCD_SendChar((char) (((i+500)/10)%10) + 0x30);

                LCD_SendChar((char) ((i+500)%10) + 0x30);

                LCD_SetPos(9,3);

                LCD_SendChar((char) (((i+750)/100)%10) + 0x30);

                LCD_SendChar((char) (((i+750)/10)%10) + 0x30);

                LCD_SendChar((char) ((i+750)%10) + 0x30);

                HAL_Delay(100);

                i++;

  /* USER CODE END WHILE */

В этом длинном коде мы для всех регистров микросхемы по очереди сначала из соответствующей ячейки буфера, в который мы данные регистры считали, сначала берём показание в переменную, затем преобразовываем в обычный десятичный вид, Затем десятки и единицы превращаем в символы в соответствии с таблицей ascii, и затем выводим на дисплей. Потом в конце применяем задержку 100 милисекунд, затем весь процесс повторяется сначала.

Остальной код из бесконечного цикла, оставшийся из проекта по тестированию и подключения дисплея, мы пока не убираем.

Соберём код и прошьём контроллер. Посмотрим, как у нас всё это работает.

 

image15

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

Техническая документация:

Техническая документация на микросхему RTC DS3231

 

Отладочную плату, дисплей LCD 20×4 и модуль RTC DS3231 с микросхемой памяти можно приобрести здесь:

STM32F4-DISCOVERY

Дисплей LCD 20×4

Модуль RTC DS3231 с микросхемой памяти (3 шт)

Модуль RTC DS3231 с микросхемой памяти (1 шт) — так дороже

 

 

Смотреть ВИДЕОУРОК

 

STM32 HAL. Шина I2C. Продолжаем работу с DS3231

22 комментария на “STM Урок 9. HAL. Шина I2C. Продолжаем работу с DS3231
  1. Phoenix75:

    Здравствуйте. Очень помогают ваши уроки. Подскажите пожалуйста вот что, в своём проекте использую eeprom в этом модуле часов, так как stm32f103c8 её не имеет.

    Зогвоздка в том что при записи или чтении почти всё время за редким исключением контролер виснет. Но это происходит только при записи или чтении при срабатывании таймера например, но всё прекрасно работает если запись или чтение происходит при нажатии кнопки.

    Вот модуль записи ичтения

    void eeprom_write_menu(uint8_t memory_adres,uint8_t znach_mem ){

    zapis_eep=1;

    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);

    HAL_I2C_Mem_Write(&hi2c1, (uint16_t) 0xAE, memory_adres,I2C_MEMADD_SIZE_16BIT,&znach_mem, 1, 100); 

    HAL_Delay(10);

    HAL_I2C_Mem_Read(&hi2c1,(uint16_t) 0xAE, 0x31 ,I2C_MEMADD_SIZE_16BIT,(uint8_t *)&mywrite, 28, 100); 

    HAL_Delay(10);

    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);

    zapis_eep=0;

    }

    На могли бы Вы дополнить урок примером для записи и чтения встроенной в модуль DS3231 епром.

    Вот моя программа может подскажите где я ошибся?

    https://cloud.mail.ru/public/9NV9/NVj61HFA3

  2. Андрей:

    Здравствуйте! Могли бы вы помочь советом?
    В подключаемом файле i2c.c вылетает ошибка по UARTу

    #include «i2c.h»
    //——————————————
    extern uint8_t aTxBuffer[8];
    //——————————————
    char str[100];
    //——————————————
    void I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf)
    {
    while(HAL_I2C_Master_Transmit(&hi, (uint16_t)DEV_ADDR,(uint8_t*) &aTxBuffer, (uint16_t)sizebuf, (uint32_t)1000)!= HAL_OK)
    {
    if (HAL_I2C_GetError(&hi) != HAL_I2C_ERROR_AF)
    {
    sprintf(str, «Buffer error»); ошибка HAL_UART_Transmit(&huart1, str, 8, 1000);
    }
    }
    }

    Вот весь проект целиком. Заранее спасибо!
    https://yadi.sk/d/Fyga3hbo3R85wY

  3. Евгений:

    Добрый день! Прекрасные уроки и практически всё понятно, но у меня вопрос по поводу ошибки при написании программного кода. Опыта в написании программы на СИ у меня мало, так как работал раньше на Бейсике. В хедере файла i2c.h при написании прототипов

    void I2C_WriteBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf);
    void I2C_ReadBuffer(I2C_HandleTypeDef hi, uint8_t DEV_ADDR, uint8_t sizebuf);
    пишет вот такую ошибку ../Inc/i2c.h(8): error: #20: identifier «I2C_HandleTypeDef» is undefined.
    Пробовал подключать stm32f4xx_hal_i2c.h, предполагая, что программа не видит эту библиотеку, но это всё бесполезно. Короче Кейл не видит функции I2C_HandleTypeDef, хоть убейся. Как можно исправить это.

  4. Евгений:

    Я сейчас пробую на другом копме проверить проблему с этим кодом. У меня Кейл версии 4, а на другом я поставил версию 5. Может в нём дело, да и Куб там по новой установлен. Проверю отпишу.

  5. Евгений:

    Добрый вечер! Хотел сразу, после проверки программного кода на Кейл 5, отписать результат работы. Результат оказался точно таким же как в Кейл 4. Куб удалил полностью и установил заново, помимо ещё и обновил его новым патчом за 2018 г. Прежде чем отписать результат, я ещё раз внимательно проделал последовательно все действия при написании кода. Я всё надеялся, что где то допустил ошибку при написании кода или указал не все пути рабочих библиотек HAL. При компиляции по прежнему всё та же ошибка error: #20: identifier «I2C_HandleTypeDef» is undefined в хедере i2c.h, к тому же до компиляции в этом же хедере да и в других местах где есть включение #include «stm32f4xx_hal.h», подчёркивает и пишет на него fatal error: «stm32f4xx_hal.h» file not found. Пробовал я даже все библиотеки из Drive, куда устанавливает их Куб при инициализации программы, прописывать напрямую в папку inc, а в Src все си, но толку никакого. Если навести наведении на HandleTypeDef и кликнуть по всплывающему меню то перекидывает в файл stm32f4xx_hal_i2c.h, где находятся типы структур. Получается что всё необходимое подключено, но как будто ничего чего не видит в этих файлах. В общем надо продвигаться дальше в учёбе, а я застрял на этом уроке из за этой ошибки.

    • Здравствуйте!
      Оно и не удивительно. Столько времени уже прошло с момента урока. И кейл и куб стали другими и многие проекты приходится перерабатывать. Спасибо за интерес!

    • Павел:

      у меня такаяже беда, почему при сборке пишет C:/Project/STM32F103RBT6-XT33-BMP180/Inc/i2c.h:14:21: error: unknown type name 'I2C_HandleTypeDef'
      пишу в System Workbench for stm32
      если вы решили эту проблему поделитесь пожалуйста решением

      • Может помочь переустановка Cube MX и библиотек, а также не помешает обновление System Workbench. Затем удалить из проекта все папки кроме Inc и Src и заново пересобрать проект в Cube MX. А в SWB также проект удалить и добавить заново.

  6. onur:

    привет народный поток.
    почему бы вам не использовать внутренний rtc?
    Каковы ваши взгляды на внутренний rtc?
    Является ли внутренний rtc хорошим (безопасным) или ds3231?
    Iam sory.I use google translate.

  7. magik:

    Здравствуйте. И у меня аналогичная проблема с I2C_InitTypeDef. Что же делать, narodstream? Просим помочь разобраться. Заранее спасибо.

  8. Вячеслав:

    Проблему с I2C_InitTypeDef можно решить собрав, функции работы с i2c в main.c

    • Михаил:

      Проблему с I2C_InitTypeDef, по совету Вячеслава, я решил переносом функций работы с i2c из i2c.с в main.c.(проверено на xNucleo F030R8).

  9. streloc:

    а как сами часы установить?
    uint8_t sec=10, min=20, hour=15, day=7, date=23, month=9, year=18;
    Не срабатывает.

    • Михаил:

      А вот начальная установка часов:
      /* USER CODE BEGIN WHILE */
      //Начальная установка часов*****************************************************
      // инициализируем буфер
      aTxBuffer[0]=0x00;
      aTxBuffer[1]=0x00; //секунды(0-59)
      aTxBuffer[2]=0x20;//минуты(0-59)
      aTxBuffer[3]=0x15;//часы(1-12+AM/PM или 00-23)
      aTxBuffer[4]=0x03; //день недели(1-7)
      aTxBuffer[5]=0x17; //дата(01-31)
      aTxBuffer[6]=0x10;//месяц(01-12)
      aTxBuffer[7]=0x18; //год(00-99)

      //передадим в функцию I2C-адрес DS3231(0xD0) и адрес первого регистра DS3231(0x00)
      I2C_WriteBuffer(hi2c1,(uint16_t)0xD0,1);
      //подождем готовность микросхемы
      while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
      {
      }
      //передадим в DS3231 подряд 7 байт из буфера
      I2C_WriteBuffer(hi2c1,(uint16_t)0xD0,7);
      //******************************************************************************
      while (1)

  10. Volderon:

    Доброго времени суток)
    Вместо часов на экране выходит только две строки прямоугольных символов. Уж и код несколько раз переписывал, и коммутацию сто раз проверил, даже перепаял контакты — все так же( Может ли быть дело в кубе? Скачал специально версию 16 года чтоб не париться с совместимостью.

  11. Александр:

    Добрый день.
    Не могу дальше двигаться. выдает 2 ошибки.
    LCD_RTC\LCD_RTC.axf: Error: L6200E: Symbol aTxBuffer multiply defined (by stm32f4xx_it.o and main.o).
    LCD_RTC\LCD_RTC.axf: Error: L6200E: Symbol aTxBuffer multiply defined (by stm32f4xx_hal_msp.o and main.o).

    • Александр:

      перенес переменную в main.с!!! вроде заработало. прочитал об ошибке на просторах паутины. но все равно не понял сути.

  12. Я запустил эти часы реального времени на регистрах правда в кубе использовал генератор кода LL библиотека.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*