Регистр 0xF2 “status”
|
Наименование |
Описание |
Бит 3 |
measuring[0] |
Автоматически устанавливается в «1» при выполнении преобразования
и обратно в «0» при передаче результата в регистр данных.
|
Бит 0 |
im_update[0] |
Автоматически устанавливается в «1» при копировании данных NVM в регистры изображений
и обратно в «0», когда копирование завершено.
|
Необходимо считывать данные, когда эти два бита сброшены. При реализации данного проекта не разу не было замечено установленного бита im_update, а вот бит bit2 данного регистра, который вообще не должен быть задействован, вёл себя как im_update!!!
Регистр reset
Регистр 0xE0 «reset» служит для сброса значений регистров датчика, если в него записать значение 0xB6.
Регистр id
В регистре 0xD0 «id» хранится уникальное значение для каждого датчика — chip_id.
Регистр калибровки
Регистры calib00..calib41 индивидуальны для каждого датчика и необходимы для преобразования «сырых» данных.
Для преобразования исходных данных для каждого вида измерений используются конкретные регистры калибровки:
Адрес регистра |
Содержание регистра |
Тип данных |
0x88 / 0x89
|
dig_T1 [7:0] / [15:8]
|
unsigned short
|
0x8A / 0x8B
|
dig_T2 [7:0] / [15:8]
|
signed short
|
0x8C / 0x8D
|
dig_T3 [7:0] / [15:8]
|
signed short
|
0x8E / 0x8F
|
dig_P1 [7:0] / [15:8]
|
unsigned short
|
0x90 / 0x91
|
dig_P2 [7:0] / [15:8]
|
signed short
|
0x92 / 0x93
|
dig_P3 [7:0] / [15:8]
|
signed short
|
0x94 / 0x95
|
dig_P4 [7:0] / [15:8]
|
signed short
|
0x96 / 0x97
|
dig_P5 [7:0] / [15:8]
|
signed short
|
0x98 / 0x99
|
dig_P6 [7:0] / [15:8]
|
signed short
|
0x9A / 0x9B
|
dig_P7 [7:0] / [15:8]
|
signed short
|
0x9C / 0x9D
|
dig_P8 [7:0] / [15:8]
|
signed short
|
0x9E / 0x9F
|
dig_P9 [7:0] / [15:8]
|
signed short
|
0xA1
|
dig_H1 [7:0]
|
unsigned char
|
0xE1 / 0xE2
|
dig_H2 [7:0] / [15:8]
|
signed short
|
0xE3
|
dig_H3 [7:0]
|
unsigned char
|
0xE4 / 0xE5[3:0]
|
dig_H4 [11:4] / [3:0]
|
signed short
|
0xE5[7:4] / 0xE6
|
dig_H5 [3:0] / [11:4]
|
signed short
|
0xE7
|
dig_H6
|
signed char
|
dig_Tx — температура, dig_Px — давление, dig_H — влажность.
Формула компенсаций
В документации описана «формула» компенсаций для перевода исходных данных:
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC. // t_fine carries fine temperature as global value BME280_S32_t t_fine; BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) { BME280_S32_t var1, var2, T; var1 = ((((adc_T>>3) – ((BME280_S32_t)dig_T1<<1))) * ((BME280_S32_t)dig_T2)) >> 11; var2 = (((((adc_T>>4) – ((BME280_S32_t)dig_T1)) * ((adc_T>>4) – ((BME280_S32_t)dig_T1))) >> 12) * ((BME280_S32_t)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } // Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits). // Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P) { BME280_S64_t var1, var2, p; var1 = ((BME280_S64_t)t_fine) – 128000; var2 = var1 * var1 * (BME280_S64_t)dig_P6; var2 = var2 + ((var1*(BME280_S64_t)dig_P5)<<17); var2 = var2 + (((BME280_S64_t)dig_P4)<<35); var1 = ((var1 * var1 * (BME280_S64_t)dig_P3)>>8) + ((var1 * (BME280_S64_t)dig_P2)<<12); var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)dig_P1)>>33; if (var1 == 0) { return 0; // avoid exception caused by division by zero } p = 1048576-adc_P; p = (((p<<31)-var2)*3125)/var1; var1 = (((BME280_S64_t)dig_P9) * (p>>13) * (p>>13)) >> 25; var2 = (((BME280_S64_t)dig_P8) * p) >> 19; p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)dig_P7)<<4); return (BME280_U32_t)p; } // Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits). // Output value of “47445” represents 47445/1024 = 46.333 %RH BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H) { BME280_S32_t v_x1_u32r; v_x1_u32r = (t_fine – ((BME280_S32_t)76800)); v_x1_u32r = (((((adc_H << 14) – (((BME280_S32_t)dig_H4) << 20) – (((BME280_S32_t)dig_H5) * v_x1_u32r)) + ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) * ((BME280_S32_t)dig_H2) + 8192) >> 14)); v_x1_u32r = (v_x1_u32r – (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)dig_H1)) >> 4)); v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r); v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r); return (BME280_U32_t)(v_x1_u32r>>12); }
Данная «формула» используется в программе BME280_monitor для преобразования принятых данных с датчика.
Простой пример настроек датчика для снятия показаний температуры, давления и влажности:
ctrl_hum = 0x01, ctrl_meas = 0x25, config = 0x00
Предлагаемые настройки для мониторинга погоды |
Режим датчика |
Forced mode, одно измерение в минуту |
Передискретизация |
температура x 1, давление x1, влажность x 1 |
IIR фильтр |
выключен |
Разбор кода микроконтроллера.
Проект состоит из 9-ти файлов:
- main.asm
- config.inc
- vars.asm
- interups_vector.asm
- interrupts.asm
- initialization.asm
- uart.asm
- twi_mp.asm
- lib.asm
main.asm
.include "tn2313adef.inc" .include "config.inc" .include "vars.asm" ;======================================================== ; Начало программного кода ;======================================================== .cseg ; Выбор сегмента программного кода .org 0 ; Установка текущего адреса на ноль start: .include "interrupts_vector.asm" .include "interrupts.asm" .include "initialization.asm" ldi YH, high(USART_data) ; куда будем записывать принятые данные от USART ldi YL, low(USART_data) ;-------------------------- Основной бесконечный цикл в ожидании прерывания sei main: sleep rjmp main .include "lib.asm" .include "twi_mp.asm" .include "uart.asm"
В данном файле подключаются необходимые зависимости, устанавливается начальный адрес ячеек памяти, в которые будут записываться принимаемые данные с USART. После запускается — бесконечный цикл, в котором микроконтроллер «спит», ожидая прерываний.
config.inc — рассматривался выше.
vars.asm
;======================================================== ; Глобальные переменные в РОН ;======================================================== .def CONST_ZERO = r0 ; постоянный ноль .def USER_REG_STATUS = r16 ; пользовательский регистр статусов ;------------------------------------------------- ;| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ;------------------------------------------------- ; бит 0 - ACK_NACK 0 - ACK, 1 - NACK ; бит 1 - счетчик принятых байтов. 0 - принимается первый байт, 1 - принимается второй байт .def BYTE = r22 ; Байт передачиприём данных ;======================================================== ; Глобальные переменные в RAM ;======================================================== .dseg ; Выбираем сегмент RAM .org 0x60 ; Устанавливаем текущий адрес сегмента ;------------------------- Хранение данных BME280_data: .byte BME280_data_size ; данные принятые с BME280 USART_data: .byte 2 ; данные принятые с USART
CONST_ZERO остался с прошлого проекта, используется как постоянный 0. USER_REG_STATUS — пользовательский регистр статусов, используются только два первых бита. Когда необходимо хранить некоторое количество флагов, которые могут быть только в двух значениях, то считается расточительством под каждый из них выделять целый регистр РОН или один байт в памяти RAM. BYTE — буферный регистр для приёма и отправки данных как по USART так и по TWI. BME280_data — массив, в который записываются данные с регистров датчика bme280. USART_data — хранение двух байт, которые принимаются с USART, а точнее с компьютера. Необходимы для определения команд от программы BME280_monitor.
interups_vector.asm
;======================================================== ; Вектор прерываний ;======================================================== rjmp reset ; Переход на начало программы reti ; Внешнее прерывание 0 reti ; Внешнее прерывание 1 reti ; Прерывание по захвату таймера T1 reti ; Прерывание по совпадению T1 A reti ; Прерывание по переполнению T1 reti ; Прерывание по переполнению T0 rjmp _UART_RX; Прерывание UART прием завершен reti ; Прерывание UART регистр данных пуст reti ; Прерывание UART передача завершена reti ; Прерывание по компаратору reti ; Прерывание по изменению на любом контакте reti ; Таймер/счетчик 1. Совпадение B reti ; Таймер/счетчик 0. Совпадение A reti ; Таймер/счетчик 0. Совпадение B reti ; USI Стартовая готовность reti ; USI Переполнение reti ; EEPROM Готовность reti ; Переполнение охранного таймера reti ; PCINT1 Handler reti ; PCINT2 Handl
В векторе прерываний используется только одно прерывание, помимо reset, прерывание — «USART прием завершен». Микроконтроллер находится в режиме ожидания пока не будет получен байт по линии USART.
interrupts.asm
;======================================================== ; Прерывание UART - приём завершен ;======================================================== _UART_RX: push r17 push r18 push BYTE in BYTE, UDR sbrs USER_REG_STATUS, 1 ; если бит установлен, rjmp _UART_RX_first_byte ; то сейчас принимается второй байт andi USER_REG_STATUS, 0xFD ; сброс бита счетчика st Y, BYTE ; запись принятого второго байта по адресу Y ldi YH, high(USART_data) ; установка указателя в начало массива ldi YL, low(USART_data) ld r17, Y ; сравниваем первый принятый байт с нулём cpi r17, 0x00 ; и если не ноль - читаем все регистры с датчика и отправляем их по USART breq _UART_RX_read_all_register_and_transmit ldd BYTE, Y+1 ; отправляем данные второго байта по адресу первого rcall twi_write_one_byte _UART_RX_end: pop BYTE pop r18 pop r17 reti ;-------------------------- получение первого байта по USART _UART_RX_first_byte: st Y+, BYTE ori USER_REG_STATUS, 0x02 ; установка бита счетчика rjmp _UART_RX_end ;-------------------------- считывание всех регистров из BME280 и отправка по USART _UART_RX_read_all_register_and_transmit: ldi XH, high(BME280_data) ; куда будем записывать принятые данные от BME280 ldi XL, low(BME280_data) ldi r17, 26 ;(0x88 - 0xA1) ldi r18, 0x88 rcall twi_read_package ldi r17, 1 ;(0xD0) ldi r18, 0xD0 rcall twi_read_package ldi r17, 17 ;(0xE0 - 0xF0) ldi r18, 0xE0 rcall twi_read_package ldi r17, 4 ;(0xF2 - 0xF5) ldi r18, 0xF2 rcall twi_read_package ldi r17, 8 ;(0xF7 - 0xFE) ldi r18, 0xF7 rcall twi_read_package ;-------------------------- отправка по USART ldi r17, 57 ; счетчик ldi XH, high(BME280_data) ; от куда будем считывать данные для отправки по USART ldi XL, low(BME280_data) transmit_all_register_loop: dec r17 breq transmit_all_register_end ld BYTE, X+ rcall USART_Transmit rjmp transmit_all_register_loop transmit_all_register_end: rjmp _UART_RX_end
В файле interrupts.asm прерывание _UART_RX проверяет принятый байт. Команды от программы BME280_monitor приходят в виде последовательности из двух байт. Первый байт — адрес регистра датчика, второй — данные для записи в регистр. Если данные с датчика необходимо считать, а не записать, программа отправляет два нуля. Для получения данных микроконтроллером с регистров датчика используется пакетное считывание. Так как последовательность адресов регистров в bme280 не является непрерывной, то считывание происходит в пять заходов. После, этот массив данных из 56 байт отправляется через USRAT в программу BME280_monitor . Программа разбирает этот массив и выводит информацию.
initialization.asm
;======================================================== ; Модуль инициализации ;======================================================== init: ;-------------------------- Инициализация стека ldi r17, RAMEND ; Выбор адреса вершины стека out SPL, r17 ; Запись его в регистр стека ;--------------------------- Инициализация компаратора ldi r17, 0x80 ; Выключение компаратора out ACSR, r17 ;-------------------------- Инициализация Главного предделителя ldi r17, 0x80 out CLKPR, r17 ldi r17, 0x03 ; Записываем 3 в регистр r17 out CLKPR, r17 ; Записываем это число в CLKPR, указывая, что мы делим частоту на 8. ;-------------------------- Инициализация портов ВВ по умолчанию (на вход с подтягивающим резистором) ser r17 out PORTA, r17 out PORTB, r17 out PORTD, r17 out DDRA, CONST_ZERO out DDRB, CONST_ZERO out DDRD, CONST_ZERO ;-------------------------- Инициализация TWI sbi twi_DDR, twi_SCL sbi twi_DDR, twi_SDA ldi r17, (0<<USISIE)|(0<<USIOIE)|(1<<USIWM1)|(0<<USIWM0)|(1<<USICS1)|(0<<USICS0)|(1<<USICLK)|(0<<USITC) out USICR, r17 ldi r17, (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|(0x0<<USICNT0) out USISR, r17 sbi twi_PORT, twi_SCL sbi twi_PORT, twi_SDA ;--------------------------- Разрешаем режим сна (idle) ldi r17, 1 << SE out MCUCR, r17 ;--------------------------- Инициализация USART ldi r17, low(bauddivider) out UBRRL, r17 ldi r17, high(bauddivider) out UBRRH, r17 out UCSRA, r17 ldi r17, (1 << RXEN) | (1 << TXEN) | (1 << RXCIE) | (0 << TXCIE) | (0 << UDRIE) | (0 << UCSZ2); передача и приём разрешены, прерывания по приёму байта включено. out UCSRB, r17 ldi r17, (0 << UMSEL0) | (0 << UMSEL1) | (1 << UCSZ0) | (1 << UCSZ1) | (0 << UPM1) | (0 << UPM0) | (0 << USBS) | (0 << UCPOL) ;формат кадра - 8 бит, пишем в регистр ucsrc, за это отвечает бит селектор out UCSRC, r17
В данном коде происходит первоначальная инициализация микроконтроллера при включении. Комментариев достаточно для понимания кода, кроме инициализация TWI и USART, но это отдельная тема. Инициализация портов вводавывода на вход с подтягивающим резистором необходима, что бы ножки контроллера не «висели» в неопределённом состоянии. Это так же влияет на потребление тока микроконтроллером, что важно для автономных устройств.
uart.asm
;======================================================== ; Процедура отправки байта из регистра BYTE ;======================================================== USART_Transmit: sbis UCSRA, UDRE ; пропуск если нет флага готовности rjmp USART_Transmit ; ждем готовности - флага udre out UDR, BYTE ; шлём байт ret
Здесь описывается только одна процедура отправки байта по USART. В текущем проекте данные по USART не передаются одновременно в два направления. Если бы обмен данных был бы интенсивней, то такой код мог бы приводить к ошибкам. Регистр BYTE используется как для приёма данных, так и для передачи. Входя в процедуру USART_Transmit может сработать прерывание на приём данных, и тогда регистр BYTE будет переписан! Это необходимо учитывать при масштабировании кода.
twi_mp.asm
;======================================================== ; TWI start ;======================================================== twi_start: push r17 sbi twi_PORT, twi_SDA sbi twi_PORT, twi_SCL cbi twi_DDR, twi_SCL ldi r17, (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC) | (0x00 << USICNT0) out USISR, r17 cbi twi_PORT, twi_SDA rcall MCU_wait_10mks sbi twi_DDR, twi_SCL cbi twi_PORT, twi_SCL sbi twi_PORT, twi_SDA rcall MCU_wait_10mks pop r17 ret ;======================================================== ; TWI stop ;======================================================== twi_stop: cbi twi_PORT, twi_SCL rcall MCU_wait_10mks cbi twi_PORT, twi_SDA rcall MCU_wait_10mks sbi twi_PORT, twi_SCL rcall MCU_wait_10mks sbi twi_PORT, twi_SDA rcall MCU_wait_10mks sbi USISR, USIPF ret ;======================================================== ; TWI master_transfer ; входящее r17 (USISR_8BIT или USISR_1BIT) ; исходящее BYTE ;======================================================== twi_master_transfer: out USISR, r17 ldi r17, (0 << USISIE) | (0 << USIOIE) | (1 << USIWM1) | (0 << USIWM0) | (1 << USICS1) | (0 << USICS0) | (1 << USICLK) | (1 << USITC) loop_send: rcall MCU_wait_10mks out USICR, r17 rcall MCU_wait_10mks out USICR, r17 sbis USISR, USIOIF rjmp loop_send rcall MCU_wait_10mks in BYTE, USIDR ldi r17, 0xff out USIDR, r17 sbi twi_DDR, twi_SDA ret ;======================================================== ; TWI send ; входящее BYTE ;======================================================== twi_send_byte: push r17 push BYTE out USIDR, BYTE ldi r17, USISR_8BIT rcall twi_master_transfer cbi twi_DDR, twi_SDA ldi r17, USISR_1BIT rcall twi_master_transfer rcall twi_set_status_bit pop BYTE pop r17 ret ;======================================================== ; TWI recive ; исходящее BYTE ;======================================================== twi_recive_byte: push r17 cbi twi_DDR, twi_SDA ldi r17, USISR_8BIT rcall twi_master_transfer push BYTE sbrs USER_REG_STATUS, twi_ACK_NACK rjmp twi_recive_ack ldi r17, 0xFF out USIDR, r17 rjmp twi_recive_end twi_recive_ack: ldi r17, 0x00 out USIDR, r17 twi_recive_end: ldi r17, USISR_1BIT rcall twi_master_transfer pop BYTE pop r17 ret ;======================================================== ; Считывание r17 регистров подряд начиная с регистра r18 ; запись в RAM по значению указателя X ; ; входящие: ; r17 - количество считываемых регистров ; r18 - адрес первого считываемого регистра ; X - адрес, куда записываются принятые результаты ;======================================================== twi_read_package: push r17 push r18 rcall twi_start ldi BYTE, BME280_addr_write rcall twi_send_byte mov BYTE, r18 rcall twi_send_byte rcall twi_start ldi BYTE, BME280_addr_read rcall twi_send_byte twi_read_package_recive: dec r17 breq twi_read_package_recive_last_byte andi USER_REG_STATUS, 0xFE ; сброс бита ACK_NACK rcall twi_recive_byte st X+, BYTE rjmp twi_read_package_recive twi_read_package_recive_last_byte: ori USER_REG_STATUS, 0x01 ; установка бита ACK_NACK rcall twi_recive_byte st X+, BYTE rcall twi_stop pop r18 pop r17 ret ;======================================================== ; Установка бита ACK_NACK в регистре USER_REG_STATUS ; входящие: ; BYTE - ответ датчика BME280 (1 или 0) ;======================================================== twi_set_status_bit: cpi BYTE, 0x00 breq twi_set_status_bit_ack ori USER_REG_STATUS, 0x01 ; установка бита ACK_NACK ret twi_set_status_bit_ack: andi USER_REG_STATUS, 0xFE ; сброс бита ACK_NACK ret ;======================================================== ; Запись одного байта ; входящие: ; r17 - адрес регистра ; BYTE - данные, которые запишутся в регистр ;======================================================== twi_write_one_byte: push BYTE rcall twi_start ldi BYTE, BME280_addr_write rcall twi_send_byte mov BYTE, r17 rcall twi_send_byte pop BYTE rcall twi_send_byte ret
В фале twi_mp.asm реализованы процедуры для работы по интерфейсу TWI в режиме «Один ведущий на шине».
lib.asm
;======================================================== ; Подпрограммы формирования задержки ;======================================================== MCU_wait_10mks: ; rcall - 3, ret - 4, nop - 1 x 3 nop nop nop ret
В данном файле реализована одна процедура — процедура задержи на 10 тактов. В проекте частота микроконтроллера равна 1 МГц, соответственно данная задержка будет равна 10 мкс.
Fuse биты микроконтроллера:

Fuse биты микроконтроллера ATTiny2313 установлены по умолчанию.
Фото устройства на макетной плате:

Интерфейс программы BME280_monitor:

Окно программы делится на четыре части: слева — таблица регистров датчика; справа — замеры, настройки, кнопки приёма/передачи и сброса; снизу — строка состояния; сверху — панель меню. Для подключения к устройству, необходимо в настройках программы выбрать нужный COM порт и установить его настройки. После в меню Calls выбрать пункт Connect. Далее следует выставить в правой панели необходимые настройки датчика и отправить их, нажав кнопку Write. Для считывания данных — кнопка Read или поставить флажок Autoread.
Ссылка на portable версию программы BME280_monitor.
|