пʼятниця, 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 шині

Передмова

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

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

Створюємо новий проект в CubeMX. Обираємо свій MCU. Позначаємо потрібні піни.
Я використовую MCU STM32F103C8T6, тож приклад буде для нього, але це не принципово. У вас може бути будь який інший MCU STM32.
Налаштування шпильок мікроконтролеру
У в кладці "Pinout" назначимо куди які виводи MCU призначаються:
  1. Оберемо шину: I2C1 (або I2C2);
  2. Назначимо, що тактуватись будемо від зовнішнього кварцу: Crystal/Ceramic Resonator (не обов'язково, можна залишити як є, тактуватись буде від внутрішнього генератора);
  3. Оберемо пристрій Debug по якому заливаємо прошивку та налагоджуємо програму: Serial Wire;
  4. Оберемо послідовну шину: USART1 (можна інший, який зручно вам).
  5. Позначемо якусь шпильку, як GPIO_OUTPUT, до якої через резистор підключимо світлодіод. В мене це PC13
З налаштуваннями це все. Можна зберігати проект:
  1.  Ім'я, наприклад, "I2C_BusScaner";
  2. Вказати шлях де буде зберігатись проект;
  3. Обрати середовище розробки.

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

Відкривайте чи експортуйте проект в своєму засобі розробки. В мене, це вже новий 
TrueSTUDIO for STM32 9.0.0 released, який підтримує лише STM32, та має PRO можливості абсолютно безкоштовно.

Відкриваємо в дереві проекту файл "main.h" і додаємо декілька defines:
/* USER CODE BEGIN Private defines */
#define LED_RED_Pin GPIO_PIN_13
#define LED_RED_GPIO_Port GPIOC
/* USER CODE END Private defines */
Або називаємо "LED_RED" шпильку PC13 в самому CubeMX, як на попередній світлині, тоді ці два рядки додадуться автоматично.

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

Додаємо до проекту бібліотеку "string.h", вона нам згодиться для роботи з текстовими рядками:
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

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

Тепер сама програма сканеру шини I2C:
/* USER CODE BEGIN 2 */
  print("\r\nUSART1 is OK\r\n");
  print("Start test I2C\r\n");

  for(int i = 0; i < 128; i++) // Так як адреса I2C пристроїв має 7 біт, скануємо адреси від 0 по 127
  {
   uint16_t address = i << 1; // Зміщуємо адресу на один біт ліворуч

   if(HAL_I2C_IsDeviceReady(&hi2c1, (uint16_t) address, 1, 1000) == HAL_OK) // Якщо по цій адресі буде відгук, то надрукуємо цю адресу
   {
    char string[10]; // Оголошуємо char масив для текстового рядку
    sprintf(string, "0x%X", address >> 1); // формуємо текстовий рядок з адресою пристрою в HEX форматі
    print("Address: ");
    print(string); // друкуємо в порт текстовий рядок
    print("\r\n"); // перехід на новий рядок
 }    
  }
  print("Test end I2C\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin); // Після завершення сканування весело блимаємо світлодіодом
  HAL_Delay(100);
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

Та не забуваємо вставити функцію, прототип якої оголосили на початку:
/* USER CODE BEGIN 4 */
void print(char string[])
{
    HAL_UART_Transmit(&huart1, (uint8_t*) string, strlen(string), 100); // Друк текстового рядку в послідовний порт
}
/* USER CODE END 4 */

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


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