Главная » Микроконтроллеры
Призовой фонд
на сентябрь 2021 г.
1. 1000 руб
Паяльник
2. Тестер компонентов MG328
Сайт Паяльник
3. 100 руб.
От пользователей

Обмен данными датчика 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.

Прикрепленные файлы:

Теги:

Опубликована: Изменена: 23.04.2021 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (6) | Я собрал (0) | Подписаться

0
Публикатор #
На форуме автоматически создана тема для обсуждения статьи.
Ответить
0
Igor #
USART и TWI - это не протоколы, а интерфейсы. Подправьте пожалуйста.
Ответить
0

[Автор]
verbkinm #
Спасибо за замечание, исправил!
Ответить
+1
andro #
Привет.
Хочу если не выучить для самостоятельного написания, то хотя бы ознакомиться для понимания уже написанных текстов ассемблера для AVR. Что посоветуете почитать?
Ответить
0

[Автор]
verbkinm #
Здравствуй! Список литературы по ссылке https://avr.ru/docs/books/textbook. Я начинал с Белов А.В. "Микроконтроллеры AVR. От азов программирования до создания практических устройств". Там рассматриваются сразу и ассемблер и Си. А так, всё упирается в документацию. Ассемблер - это же не конкретный язык! Я его использую только под AVR.
Отредактирован 27.05.2021 09:22
Ответить
0
andro #
Да, меня тоже интересует ассемблер под АВР, спасибо, буду читать.
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

AVR-программатор USB ASP
AVR-программатор USB ASP
Металлоискатель MD3010II Pickit 2 - USB-программатор PIC-микроконтроллеров
вверх