Регистр 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.
|
Комментарии (6)
|
Я собрал (0) |
Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]
Хочу если не выучить для самостоятельного написания, то хотя бы ознакомиться для понимания уже написанных текстов ассемблера для AVR. Что посоветуете почитать?
[Автор]