
Опрос датчика температуры AM2320 по двум проводам i2c с помощью stm32f0.
Я уже писал как получить данные с датчика AM2320 по 1-wire и 2-wire с stm32f030.
Для семейства stm32f0 работа по протоколу i2c без HAL отличается.
Код писался и компилировался в CooCox CoIDE.
Для настройки регистра TIMINGR в документации к МК таблица со значениями. У меня МК работает на частоте 48Мгц, мне нужно 100кГц.
Значение будет
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(¤t)) { // прочитано } else { // ошибка }
На этом думаю и все.
При копировании материалов ссылка на https://terraideas.ru/ обязательна
Комментарии к статье: Подключение AM2320 по I2C к STM32F030C8T6