Обмен данными датчика BME280 c ПК

В данной статье описан процесс взаимодействия компьютера с датчиком температуры, давления и влажности BME280 (ссылка на Ali) через микроконтроллер ATtiny2313 на языке ассемблер. Для того, чтобы использовать датчик в более сложных проектах (мобильная метеостанция и пр.), необходимо изучить его архитектуру, настройки и способы получения измерений.

Диапазоны измерений датчика:

Температура -40 .. +85 C° 
Влажность 0 .. 100%
Давление

300 .. 1100 hPa

Задача: обмен данными компьютера с датчиком bme280 и их обработка. 

Необходимые компоненты: модуль датчика bme280, микроконтроллер с TWI и USART, адаптер USB TTL с питанием 3.3 В для взаимодействия компьютера и микроконтроллера.

Модуль датчика BME280

Микроконтроллер семейства tiny с интерфейсами

TWI и USART (ATTiny2313A)

Адаптер USB TTL
BME280 ATTiny2313A USB-tty

Также необходима программа BME280_monitor. Ссылка в конце статьи.

Подключение: SDA и SCL ножки датчика bme280 подключаем к ножкам с таким же наименование на микроконтроллере. Ножки RX и TX микроконтроллера подключаем к ножкам TX и RX адаптера USB TTL. Адаптера USB TTL подключаем к компьютеру.

Схема Proteus

В Proteus датчик bme280 реализован без симуляции, схема подключения для наглядности. Вместо подключения к адаптеру  USB TTL — подключение к виртуальному терминалу.

Для взаимодействия микроконтроллера с датчиком bme280 используется интерфейс I2C (TWI). В микроконтроллерах семейства tiny нет готового TWI, для этого служит модуль USI, который настраивается в режиме TWI. Для взаимодействия микроконтроллера с адаптером USB TTL используется интерфейс USART. Настройка USI TWI и USART темы для отдельных статей и здесь описываться не будут.

Код данного проекта можно легко изменить для любой модели микроконтроллеров семейства tiny, у которых есть интерфейсы USI и USART, для этого рассмотрим файл config.inc

 .equ 	XTAL = 1000000 .equ 	baudrate = 4800 .equ 	bauddivider = XTAL / (16 * baudrate) - 1  .equ	BME280_addr_read	= 0xED .equ	BME280_addr_write	= 0xEC .equ	BME280_data_size	= 56  .equ	twi_PORT	=	PORTB .equ	twi_DDR		=	DDRB .equ	twi_SCL		=	PB7 .equ	twi_SDA		=	PB5 .equ	twi_PIN		=	PINB  .equ	twi_ACK_NACK=	0 .equ	ACK		    =	0 .equ	NACK		=	1  .equ	USISR_8BIT	=	0xF0 .equ	USISR_1BIT	=	0xFE

Первые три строки необходимы для работы интерфейса USART. В данном примере используется частота микроконтроллера в 1Мгц. Скорость для USART выбрана 4800 бод/сек. На данной скорости самый минимальный процент ошибки при скорости микроконтроллера в 1 МГц. Если вы захотите изменить скорость для своих целей, то не забудьте изменить код в файле lib.asm. В этом файле описана одна процедура — задержка на 10мкс. Пятая и шестая строка — адреса датчика bme280. Первый для чтения из датчика, а второй для записи в датчик. Седьмая строка указывает размер считываемых данных. 56 — количество регистров датчика. Строки с девятой по тринадцатую описывают порты и ножки микроконтроллера для работы с TWI. Эти строки и надо менять под свой контроллер. Остальные строки нужны для работы USI в режиме TWI.

Теперь более подробно о датчике BME280.

Датчик может работать в трёх режимах: Sleep mode, Forced mode и Normal mode. В режиме sleep mode датчик не проводит никаких замеров, все регистры доступны для чтения и записи. В режиме Forced mode датчик делает один замер согласно выставленных настроек и возвращается в режим Sleep mode. В режиме Normal mode датчик делает измерения с установленной периодичностью. 

Для более точного измерения настройки датчика позволяют использовать передискретизацию и IIR-фильтр. Чем выше уровень передискретизации и фильтрации, тем точнее и дольше идут замеры. Каждый вид измерения можно включать и выключать. К примеру, можно измерять только одну температуру или температуру и давление.

Теперь рассмотрим регистры датчика согласно руководства:

Таблица регистров датчика bme280

Для считывания данных температуры и давления предназначено по три регистра (temp_msb, temp_lsb, temp_xlsb — температура, press_msb, press_lsb, press_xlsb — давление), а для влажности — два (hum_msb и hum_lsb). Для настроек используется три регистра: config, ctrl_meas и ctrl_hum.

 

Регистр настроек ctrl_hum:

Регистр 0xF2 ctrl_hum

Наименование Описание
Биты 2, 1, 0
osrs_h[2:0] Режим передискретизации для измерения влажности

Настройка osrs_h:

   
orsr_h[2:0] Передискретизация влажности
000 измерения отключены
001 передискретизация x 1
010 передискретизация x 2
011 передискретизация x 4
100 передискретизация x 8
101, другие передискретизация x 16
   

 

Регистр настроек ctrl_meas:

Регистр 0xF4 ctrl_meas

Наименование Описание
Бит 7, 6, 5 osrs_t[2:0] Режим передискретизации для измерения температуры
Бит 4, 3, 2 osrs_p[2:0] Режим передискретизации для измерения давления
Бит 1, 0 mode[1:0] Выбор режима работы датчика

Настройка osrs_t:

   
osrs_t[2:0] Передискретизация температуры
000 измерения отключены (значение по умолчанию 0x80000)
001 передискретизация x 1
010 передискретизация x 2
011 передискретизация x 4
100 передискретизация x 8
101, другие передискретизация x 16

Настройка osrs_p:

   
osrs_p[2:0] Передискретизация давления
000 измерения отключены (значение по умолчанию 0x80000)
001 передискретизация x 1
010 передискретизация x 2
011 передискретизация x 4
100 передискретизация x 8
101, другие передискретизация x 16

Настройка mode:

   
mode[1:0] Режим работы датчика
00 Sleep mode
01 или 10 Forced mode
11 Normal mode

 

Регистр настроек config:

Регистр 0xF5 config

Наименование Описание
Бит 7, 6, 5 t_sb[2:0] Time standby. Контроль интерактивной задержки в режиме Normal mode
Бит 4, 3, 2 filter[2:0] Контроль IIR фильтра
Бит 0 spi3w_en[0] Включение интерфейса SPI

Настройка t_sb:

   
t_sb[2:0] Time standby [ms]
000 0.5
001 62.5
010 125
011 250
100 500
101 1000
110 10
111 20

Настройка filter:

   
filter[2:0] Коэффициент фильтрации
000 Фильтр выключен
001 2
010 4
011 8
100, другие 16

 

Регистр status:

Данный регистр служит для считывания статуса измерений.

Регистр 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-ти файлов:

  1. main.asm
  2. config.inc
  3. vars.asm
  4. interups_vector.asm
  5. interrupts.asm
  6. initialization.asm
  7. uart.asm
  8. twi_mp.asm
  9. 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 так и по TWIBME280_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 биты

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

Фото устройства на макетной плате:

 

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

bme280_monitor

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

Ссылка на portable версию программы BME280_monitor.