Подключение AM2320 по I2C к STM32F030C8T6

Подключение AM2320 по I2C к STM32F030C8T6

Опрос датчика температуры AM2320 по двум проводам i2c с помощью stm32f0.

Я уже писал как получить данные с датчика AM2320 по 1-wire и 2-wire с stm32f030.

Для семейства stm32f0 работа по протоколу i2c без HAL отличается.

Код писался и компилировался в CooCox CoIDE.

Для настройки регистра TIMINGR в документации к МК таблица со значениями. У меня МК работает на частоте 48Мгц, мне нужно 100кГц.

STM32F030C8T6 timingr 100kHz 48Mhz i2c

STM32F030C8T6 timingr register setting

Значение будет

I2C1->TIMINGR = 0x13 | (0xF << 8) | (0x2 << 16) | (0x4 << 20) | (0xB << 28);

Я же пошел другим путем, в CubeMX ввел частоту 100кГц и получил значение 0x20303E5D. Буду следовать рекомендациям куба.

Файл am2320.h

#ifndef _AM2320_H
#define _AM2320_H

#include "main.h"

#define AM_LEN 8

#define AM_PORT GPIOB
#define AM_SCL GPIO_PinSource6
#define AM_SDA GPIO_PinSource7
#define AM_PIN_SCL GPIO_Pin_6
#define AM_PIN_SDA GPIO_Pin_7

#define AM_I2C I2C1

#define AM_ADDR 0xB8

#define AM2320_CMD_READ_REG 0x03
#define AM2320_CMD_WRITE_REG 0x10

#define AM2320_ADDR_HH 0x00
#define AM2320_ADDR_LH 0x01
#define AM2320_ADDR_HT 0x02
#define AM2320_ADDR_LT 0x03

typedef struct {
	int16_t h;
	int16_t t;
} climate_s;

#endif

Файл am2320.c

#include "am2320.h"

void AM2320Init(void) {
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  GPIO_InitTypeDef port;
  GPIO_StructInit(&port);

  port.GPIO_Mode = GPIO_Mode_AF;
  port.GPIO_Pin = AM_PIN_SCL | AM_PIN_SDA;
  port.GPIO_Speed = GPIO_Speed_50MHz;
  port.GPIO_OType = GPIO_OType_OD;
  port.GPIO_PuPd = GPIO_PuPd_UP;

  // для I2C1 ноги настроим на альтернативную функцию 1
  GPIO_PinAFConfig(AM_PORT, AM_SCL, GPIO_AF_1);
  GPIO_PinAFConfig(AM_PORT, AM_SDA, GPIO_AF_1);

  GPIO_Init(AM_PORT, &port);

  I2C_InitTypeDef i2c;
  I2C_StructInit(&i2c);
  I2C_DeInit(AM_I2C);

  i2c.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
  i2c.I2C_DigitalFilter = 0;
  i2c.I2C_Mode = I2C_Mode_I2C;
  // Любой адрес, так как МК в режиме мастера, он не используется
  i2c.I2C_OwnAddress1 = 0x38;
  i2c.I2C_Ack = I2C_Ack_Enable;
  i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  // 100kHz описание выше в тексте
  i2c.I2C_Timing = 0x20303E5D;

  I2C_Init(AM_I2C, &i2c);
  I2C_Cmd(AM_I2C, ENABLE);
}

// функция из datasheet AM2320
unsigned short AM2320crc16(unsigned char *ptr, unsigned char len)
{
  unsigned short crc =0xFFFF;
  unsigned char i;
  while(len--) {
    crc ^= *ptr++;
    for(i = 0; i < 8; i++)
    {
      if(crc & 0x01) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }
  return crc;
}

int8_t AM_Read(climate_s* temp) {
  uint8_t buf[AM_LEN];
  volatile uint16_t _t;

  // не обязательно, перезапускаю I2C
  // на случай если он завис
  I2C_Cmd(AM_I2C, DISABLE);
  I2C_Cmd(AM_I2C, ENABLE);

  // ждем если занят
  I2C_WAIT_FLAG(AM_I2C, I2C_FLAG_BUSY, SET);
  // генерируем старт и передаем адрес I2C AM2320
  I2C_TransferHandling(AM_I2C, AM_ADDR, 0, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);

  // по datasheet ждать нужно по меньшей мере 800 мкс, максимум 3 мс
  delay_ms(3);

  // сигнал стоп
  I2C_TransferHandling(AM_I2C, AM_ADDR, 0, I2C_AutoEnd_Mode, I2C_Generate_Stop);

  // ждем флаг сигнала стоп
  I2C_WAIT_FLAG(AM_I2C, I2C_ISR_STOPF, RESET);

  // по datasheet датчик должен был проснуться
  // но на практике датчик не всегда с первой попытки отвечал
  // по этому я обернул в цикл начала опроса датчика
  
  _t = I2C_DELAY;
  do {
    if (I2C_GetFlagStatus(AM_I2C, I2C_ISR_STOPF)) {
      // очищаем флаг
      I2C_ClearFlag(AM_I2C, I2C_ICR_STOPCF);
      // теперь попытка начать опрос
      // шлем сигнал START
      I2C_TransferHandling(AM_I2C, AM_ADDR, 3, I2C_AutoEnd_Mode, I2C_Generate_Start_Write);
    }
    if ((--t) == 0) {
      return ERROR;
    }
  } while (!I2C_GetFlagStatus(AM_I2C, I2C_ISR_TXIS));

  // регистр отправки данных свободен?
  I2C_WAIT_FLAG(AM_I2C, I2C_ISR_TXE, RESET);
  // шлем команду "прочесть"
  I2C_SendData(AM_I2C, AM2320_CMD_READ_REG);
  
  I2C_WAIT_FLAG(AM_I2C, I2C_ISR_TXE, RESET, 5);
  // начиная со старшего байта влажности
  I2C_SendData(AM_I2C, AM2320_ADDR_HH);

  I2C_WAIT_FLAG(AM_I2C, I2C_ISR_TXE, RESET, 6);
  // 4 байта
  // AM_LEN содержит длину получаемых данных
  // команда + длинна данных + наши данных + crc
  I2C_SendData(AM_I2C, AM_LEN - 4);

  I2C_WAIT_FLAG(AM_I2C, I2C_ISR_STOPF, RESET, 7);
  I2C_ClearFlag(AM_I2C, I2C_ICR_STOPCF);

  I2C_WAIT_FLAG(AM_I2C, I2C_FLAG_BUSY, SET, 8);
  // переходим в режим чтения
  I2C_TransferHandling(AM_I2C, AM_ADDR, AM_LEN, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);

  for (uint8_t i = 0; i < AM_LEN; ++i) {
    // ждем байт от датчика
    I2C_WAIT_FLAG(AM_I2C, I2C_ISR_RXNE, RESET, 9);
    // и кладем в буфер
    buf[i] = I2C_ReceiveData(AM_I2C);
  }

  I2C_WAIT_FLAG(AM_I2C, I2C_ISR_STOPF, RESET, 10);
  I2C_ClearFlag(AM_I2C, I2C_ICR_STOPCF);

  // наша влажность
  temp->h = (buf[2] << 8) | buf[3];

  // по дш старший бит говорит о том, что температура отрицательная
  // откинем его
  temp->t = ((buf[4] << 8) | buf[5]) & 0x7fff;
  // а теперь умножим на -1 если он есть
  if (buf[4] & (1 << 7)) {
    temp->t *= -1;
  }

  // проверим crc
  return AM2320crc16(&buf[0], AM_LEN -2) == buf[6] | (buf[7] << 8);
}

Макрос ожидания флага

#define I2C_DELAY 600
#define I2C_WAIT_FLAG(i2c, f, s, err) _t = I2C_DELAY;\
	while (I2C_GetFlagStatus(i2c, f) == s) {\
		if (!(--_t)) return err;\
	}

Использовать можно так

int main(void) {
  ...
  AM2320Init();
  ...
  climate_t current;
  if (AM2320Read(&current)) {
    // прочитано
  } else {
    // ошибка
  }

На этом думаю и все.

При копировании материалов ссылка на https://terraideas.ru/ обязательна

Комментарии к статье: Подключение AM2320 по I2C к STM32F030C8T6

Нет ни одного комментария. Будьте первым!