пʼятницю, 26 січня 2018 р.

STM32: Бібліотека сенсору освітлення TSL2561 шина I2C

Передмова

Переробив бібліотеку з SparkFun TSL2561 library що для Arduino IDE на бібліотеку для STM32 використовуючи HAL Driver в роботі з I2C, все інше стандартний код Сі.

Датчик світла TSL2561 є світло-цифровими перетворювачем, який перетворює інтенсивність світла в цифровий вид. Керування по шині I2C TSL2561. Пристрій поєднує в собі один широкосмуговий фотодіод (видимий плюс інфрачервоний) та один інфрачервоний фотодіод на єдиній інтегрованій схемі CMOS. Два інтегрованих АЦП перетворюють фотодіодні потоки до цифрового виду. Ці цифрові данні можуть бути введені в мікропроцесор де вже визначається освітленість (рівень освітлення) у люксах використовуючи емпіричну формулу для наближення реакції людини на очі.

Що потрібно


  • Плата STM32F103RB Nucleo;
  • Сенсор TSL2561;
  • два резистори 10К для підтяжки шини I2C;
  • чотири дроти для з'єднання.

Зручність цієї плати в тім, що данні з сенсору можна передавати до термінальної програми на ПК по USART2 штатним засобом, без додаткового обладнання і дротів. Приєднавши до ПК плату STM32F103RB Nucleo USB шнурком отримуєте ST-LinkV2, Flash disk, UART (COM port) в одному флаконі. 
Сенсор освітлення приєднаємо до шини I2C2 MCU. Під'єднаємо два резистори для підтяжки SDA і SCL до логічної 1 (+Vcc живлення). І напишемо програму яка буде виводити в послідовний порт USART2 данні про освітлення.

Схема підключення

Схема підключення сенсора по I2C
На малюнку зображений сенсор BMP180, але це не має значення, головне показано як підключити до I2C шини. З TSL2561 все так само.

Створюємо проект в CubeMX

Створюємо новий проект в програмі CubeMX, обираємо свій MCU, назначаємо потрібні інтерфейси - SYS, I2C, USART, як на малюнку:
Налаштування шпильок
Даємо ім'я проекту, наприклад "STM32F103RB_I2C_UART_TSL2561", обираємо засіб розробки, наприклад "Atolic True Studio", та генеруємо код. З CubeMX це все.

Додаємо бібліотеку до проекту і пишемо тестову програму

Відкриваємо/експортуємо підготовлений, програмою CubeMX, проект в своєму засобі розробки.

Бібліотеку TSL2561 можна завантажити тут. Просто додайте заголовний файл бібліотеки "tsl2561.h" до теки "inc" проекту, а сирцевий файл бібліотеки "tsl2561.с" додайте до теки "src" проекту. З додаванням бібліотеки це все.

Відкриваємо головний файл проекту "main.c". І пишемо там такий код, у визначених ділянках для користувача програмою CubeMX.

Додаємо потрібні бібліотеки:
/* USER CODE BEGIN Includes */
#include "tsl2561.h"
#include <string.h>
#include <stdlib.h>
/* USER CODE END Includes */

Оголошуємо прототип функції друку текстових рядків до USART:
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
void print(char string[]);
/* USER CODE END PV */

Оголошуємо декілька змінних:
/* USER CODE BEGIN 1 */
 bool gain;        // Gain setting, 0 = X1, 1 = X16;
 unsigned int ms;  // Integration ("shutter") time in milliseconds
  /* USER CODE END 1 */

Друкуємо в послідовний порт USART текст, читаємо ID сенсору, ініціалізуємо сенсор та вмикаємо його:
/* USER CODE BEGIN 2 */
  print("\r\nUSART2 is OK\r\n");

  unsigned char ID;

  if (TSL2561_getID(&ID))
  {
   char string[2];
   sprintf(string, "%x", ID);

      print("Got factory ID: 0X");
      print(", should be 0X5X\r\n");
  }
  else
  {
     print("error get ID\r\n");
  }

  gain = 0;
  unsigned char time = 2;

  TSL2561_setTiming_ms(gain, time, &ms);
  TSL2561_setPowerUp();
  /* USER CODE END 2 */

В безкінечному циклі раз на секунду читаємо значення регістрів сенсору і вираховуємо значення "lux", та друкуємо все в послідовний порт:
/* USER CODE BEGIN WHILE */
  while (1)
  {
   char string[100];
   unsigned int data0, data1;

   if (TSL2561_getData(&data0, &data1))
   {
    double lux;    // Resulting lux value
    bool good;  // True if neither sensor is saturated

    // Perform lux calculation:
    good = TSL2561_getLux(gain, ms, data0, data1, &lux);

    // Print out the results:
    print("Data0: ");
    sprintf(string, "%d", data0);
    print(string);
    print(" -> ");
    print("Data1: ");
    sprintf(string, "%d", data1);
    print(string);
    print(" -> ");
    print("Lux: ");
    itoa((int) lux, string, 10);
    print(string);
    if (good) print(" (good)\r\n"); else print(" (BAD)\r\n");
  }
  else
  {
   // getData() returned false because of an I2C error, inform the user.
   print("error get lux\r\n");

  }
   HAL_Delay(1000);
  /* USER CODE END WHILE */

Не забуваймо додати саму функцію "print", прототип якої ми оголосили на початку програми:

/* USER CODE BEGIN 4 */
void print(char string[])
{
 HAL_UART_Transmit(&huart2, (uint8_t*) string, strlen(string), 100);
}
/* USER CODE END 4 */

Результат

Компілюємо програму, заливаємо готову прошивку до MCU, в термінальній програмі на ПК спостерігаємо результат:
Приклад роботи

Файли

понеділок, 22 січня 2018 р.

STM32: Сканер пристроїв на I2C шині (оновлено 24.12.2020)

update 24.12.2020: статтю оновлено до сучасних реалій. Додано відео-посібник.

Передмова

Розбирався я з шиною I2C, точніше хотів під'єднати сенсор освітлення TSL2561, який працює по шині I2C. І щоб зрозуміти, як тим сенсором скористуватись, довелось розбиратись з шиною I2C. Поки розбирався, вивчав, як воно все то працює і як наслідок написав невеличку програмку для сканування адрес пристроїв які "висять" на шині I2C. Зручно перевіряти адреси пристроїв, бо не завжди впевнений в правильності адреси, або переконатись чи взагалі, той, чи інший пристрій, відгукується на запит. Бо був випадок. Ніяк не міг запустити на іншому проекті пов'язаному з ESP8266 сенсор si7021. Як виявилось, він взагалі не реагує на запити. Більш за все бракований, або зіпсовався, коли припаював дроти до нього.

Створюємо проект в STM32CubeIDE

Створюємо новий проект в STM32CubeIDE. Обираємо свій MCU. Позначаємо потрібні піни. Я використовую MCU STM32F103C8T6, тож приклад буде для нього, але це не принципово. У вас може бути будь який інший MCU STM32.

Налаштування проекту
  1. Оберемо шину: I2C1 (або I2C2);
  2. Назначимо, що тактуватись будемо від зовнішнього кварцу: Crystal/Ceramic Resonator (не обов'язково, можна залишити як є, тактуватись буде, в цьому випадку, від внутрішнього генератора мікроконтролера);
  3. Оберемо пристрій Debug по якому заливаємо прошивку та налагоджуємо програму: Serial Wire;
  4. Оберемо послідовну шину: USART2 (можна інший, який зручно вам).
З налаштуваннями це все. Можна генерувати проект "Ctrl+S".

Пишемо програму

Відкриваємо в дереві проекту файл "main.c". В визначених, для користувача/розробника, ділянках додаємо такий код.

Додаємо до проекту бібліотеку "stdio.h", вона нам згодиться для друку повідомлень в Serial Monitor за допомоги функції "printf()":
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

Робимо редирект потоку до UART за допомоги переназначення системної функції "write":
/* USER CODE BEGIN 0 */
int _write(int file, char *ptr, int len)
{
	HAL_UART_Transmit(&huart2, (uint8_t *) ptr, len, HAL_MAX_DELAY);
	return len;
}
/* USER CODE END 0 */

Тепер сама програма сканеру шини I2C:
/* USER CODE BEGIN WHILE */
  while (1)
  {
	  printf("\r\nStart scanner I2C bus\r\n");

	  for (int i = 0; i < 128; ++i)
	  {
		uint16_t address = i << 1;

		if (HAL_I2C_IsDeviceReady(&hi2c1, address, 1, HAL_MAX_DELAY) == HAL_OK)
		{
			printf("Address: 0x%X\r\n", address >> 1);
		}
	  }

	  printf("End test I2C bus\r\n");
	  HAL_Delay(5000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

Компілюємо, заливаємо. Підключаємо до шини I2C MCU ваші пристрої I2C, та конвертер USART2USB до зазначеного USART по такій схемі (шпильки MCU мають відповідати назначеним шпилькам в STM32CubeIDE):
Схема підключення

В будь якій термінальній програмі слухаємо COM порт до якого підключений USART2USB конвертер.  Подаємо живлення на MCU і в термінальній програмі бачимо результат сканування:

Результат роботи програми

Я використовую вбудований в STM32CubeIDE термінал. "З коробки" Serial Monitor не встановлено, потрібно встановити додатково з репозиторію. Як це зробити, описано в статті.

Відео-посібник