//	Магнитометр (тесламетр)					//	Это датчик измеряющий магнитную индукцию, он определяет положение устройства в пространстве отностиельно магнитных полюсов земли.
											//	Эти данные можно использовать, например, для определения северного полюса земли
											//
#ifndef iarduino_Position_BMX055_BMM_h		//	Функции работы с магнитометром модуля BMX055
#define iarduino_Position_BMX055_BMM_h		//
//	КОНСТАНТЫ МАГНИТОМЕТРА					//
#define	BMM						50			//	Константа используемая в качестве аргумента при объявлении объекта для работы с магнитометром
#define	BMM_ERR_ID				51			//	ошибка возвращаемая функцией test(): датчик не найден
#define	BMM_ERR_ST				52			//	ошибка возвращаемая функцией test(): выявлены неисправности при самотестировании
#define	BMM_MG					53			//	Аргумент функции read()          указывающий выводить данные в миллигаусах
#define	BMM_MCT					54			//	Аргумент функции read()          указывающий выводить данные в микротеслах
#define	BMM_2Hz					55			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания  2 Гц, скорость передачи данных  2 Гц)
#define	BMM_6Hz					56			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания  6 Гц, скорость передачи данных  6 Гц)
#define	BMM_8Hz					57			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания  8 Гц, скорость передачи данных  8 Гц)
#define	BMM_10Hz				58			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания 10 Гц, скорость передачи данных 10 Гц)
#define	BMM_15Hz				59			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания 15 Гц, скорость передачи данных 15 Гц)
#define	BMM_20Hz				60			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания 20 Гц, скорость передачи данных 20 Гц)
#define	BMM_25Hz				61			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания 25 Гц, скорость передачи данных 25 Гц)
#define	BMM_30Hz				62			//	Аргумент функции setBandwidths() указывающий полосу пропускания в Hz - Гц (полоса пропускания 30 Гц, скорость передачи данных 30 Гц)
#define	BMM_LOW_PWR				63			//	Аргумент функции setScale()      использовать предустановленный экономичный режим (полоса пропускания 10 Гц,  3 выборки для осей XY,  3 выборки для оси Z, шумы по осям X/Y/Z = 1.0/1.0/1.4 мкТл, среднее потребление тока 0.17 мА)
#define	BMM_REGULAR				64			//	Аргумент функции setScale()      использовать предустановленный обычный     режим (полоса пропускания 10 Гц,  9 выборок для осей XY, 15 выборок для оси Z, шумы по осям X/Y/Z = 0.6/0.6/0.6 мкТл, среднее потребление тока 0.50 мА)
#define	BMM_ENHANCED			65			//	Аргумент функции setScale()      использовать предустановленный улучшенный  режим (полоса пропускания 10 Гц, 15 выборок для осей XY, 27 выборок для оси Z, шумы по осям X/Y/Z = 0.5/0.5/0.5 мкТл, среднее потребление тока 0.80 мА)
#define	BMM_HIGH				66			//	Аргумент функции setScale()      использовать предустановленный высокоточн. режим (полоса пропускания 20 Гц, 47 выборок для осей XY, 83 выборки для оси Z, шумы по осям X/Y/Z = 0.3/0.3/0.3 мкТл, среднее потребление тока 4.90 мА)
#define	BMM_RESET				67			//	Аргумент функции setFastOffset() сбросить компенсацию смещения данных (параметр BMM или отсутсвие параметра указывает учесть текущие данные для компенсации)
//	АДРЕС И ID ЧИПА:						//
#define	BMM_ADDRES				0x13		//	Адрес датчика на шине I2C
#define	BMM_ID					0x32		//	ID датчика
//	РЕГИСТРЫ ЧИПА:							//											|	бит7	|	бит6	|	бит5	|	бит4	|	бит3	|	бит2	|	бит1	|	бит0	|при вкл|	Комментарий
#define	REG_BMM_CHIPID			0x40		//	ID чипа									|											ID<7:0>												| 0x32	|	ID<7:0>			- значение регистра должно быть равно 0x32 (константа BMM_ID). ID чипа может быть прочитан только в том случае, если установлен бит управления мощностью pwr_control (регистр 0x4B бит 0).
#define	REG_BMM_X_LSB			0x42		//	Данные магнитного поля по оси X			|						x_lsb<4:0>							| -	- -	- -	- -	- -	- -	|x_self_test| N/A	|	x_lsb<4:0>		- младшие биты данных оси X, x_self_test - флаг результата самотестирования для оси X.
#define	REG_BMM_X_MSB			0x43		//	Данные магнитного поля по оси X			|						x_msb<12:5>																| N/A	|	x_msb<12:5>		- старшие биты данных оси X.
#define	REG_BMM_Y_LSB			0x44		//	Данные магнитного поля по оси Y			|						y_lsb<4:0>							| -	- -	- -	- -	- -	- -	|y_self_test| N/A	|	y_lsb<4:0>		- младшие биты данных оси Y, y_self_test - флаг результата самотестирования для оси Y.
#define	REG_BMM_Y_MSB			0x45		//	Данные магнитного поля по оси Y			|						y_msb<12:5>																| N/A	|	y_msb<12:5>		- старшие биты данных оси Y.
#define	REG_BMM_Z_LSB			0x46		//	Данные магнитного поля по оси Z			|						y_lsb<6:0>													|z_self_test| N/A	|	z_lsb<6:0>		- младшие биты данных оси Z, z_self_test - флаг результата самотестирования для оси Z.
#define	REG_BMM_Z_MSB			0x47		//	Данные магнитного поля по оси Z			|						y_msb<14:7>																| N/A	|	z_msb<14:7>		- старшие биты данных оси Z.
#define	REG_BMM_TEMP_LSB		0x48		//	Данные сопротивления термодатчика		|										r_hall_lsb<5:0> 				| -	- -	- -	| data_ready| N/A	|	r_hall_lsb<5:0>	- младшие биты беззнакового значения сопротивления для измерения температуры датчика Холла, data_ready - флаг прерывания готовности данных (флаг устанавливается если данные магнитного поля и сопротивления обновлены, но еще ничего не прочитано).
#define	REG_BMM_TEMP_MSB		0x49		//	Данные сопротивления термодатчика		|										r_hall_msb<13:6> 										| N/A	|	r_hall_msb<13:6>- старшие биты беззнакового значения сопротивления для измерения температуры датчика Холла.
#define	REG_BMM_INT_STATUS		0x4A		//	Флаги состояния прерываний				|  overrun	| overflow	| high_int_z| high_int_y| high_int_x| low_int_z	| low_int_y	| low_int_x	| 0x00	|	overrun - флаг перерасхода данных сигнализирует что данные обновляются в то время как предыдущие еще не были прочитаны, overflow - флаг переполнения сигнализирует что данные по одной из осей вышли за предел диапазона измерений, high_int_zyx - флаги сигнализируют что данные по оси ZYX превысили значение высокопорогового прерывания, low_int_zyx - флаги сигнализируют что данные по оси ZYX опустились ниже значения низкопорогового прерывания.
#define	REG_BMM_CTRL_0			0x4B		//	Управление питанием и перезагрузка		|softreset 1| -	- -	- -	- -	- -	- -	- -	- -	- -	- -	- -	- -	|	spi3	|softreset 0|pwr_control| 0x01	|	softreset<1:0> - установка в «11» приводит к программной перезагрузке, spi3 - флаг выбора 3-проводного режима шины SPI: «1» 3-проводной режим; «0» 4-проводный режим, pwr_control - бит управления мощьностью: «0» - режим приостановки (suspend); «1» - спящий режим (sleep); в активный режим (Normal) нормальный или Forced (принудительный) можно перейти только из спящего (sleep) режима установкой битов op_mode<1:0> следующего регистра.
#define	REG_BMM_CTRL_1			0x4C		//	Режимы работы, скорость и тестирование	|advanced_self_test<1:0>|			data_rate<2:0>			|	  op_mode<1:0>		| self_test	| 0x06	|	advanced_self_test<1:0> - подача тока на обмотку намотанную вогруг датчика Холла которая создаёт магнитное поле в 100 мкТ на оси Z для проведения расширенного самотестирования: «00» - отключено; «10» отрицательное поле; «11» положительное поле, data_rate<2:0> - скорость передачи данных (ODR): «000»-10Гц (по умолчанию); «001»-2Гц; «010»-6Гц; «011»-8Гц; «100»-15Гц; «101»-20Гц; «110»-25Гц; «111»-30Гц, op_mode<1:0> - режимы работы магнитометра: «00»-Normal (нормальный активный режим) данные читаются постоянно со скоростью data_rate (ODR); «01»-Forced (принудительный активный режим) после входа в указанный режим данные однократно читаются записываются в регистры данных и устройство само переходит в спящий режим; «11»-Sleep спящий режим, self_test - бит запуска обычного самотестирования.
#define	REG_BMM_INT_EN_0		0X4D		//	Флаги разрешения прерываний				| overrun_en|overflow_en| h_int_z_en| h_int_y_en| h_int_x_en| l_int_z_en| l_int_y_en| l_int_x_en| 0x3F	|	overrun_en - флаг разрешает установку флага overrun по прерыванию перерасхода данных, overflow_en - флаг разрешает направить прерывание overflow (переполнение) на вывод int, h_int_zyx_en - флаги (активный уровень 0) разрешают использовать оси ZYX для высокопорогового прерывания, l_int_zyx_en - флаги (активный уровень 0) разрешают использовать оси ZYX для низкопорогового прерывания.
#define	REG_BMM_INT_EN_1		0x4E		//	Флаги разрешения прерываний				|pin_dtrd_en| pin_int_en| channel_z	| channel_y	| channel_x	| dr_polar	| int_latch	| int_polar	| 0x07	|	pin_dtrd_en - флаг разрешает работу вывода прерывания dtrd (data ready - готовность данных), pin_int_en - флаг разрашает работу вывода int, channel_zyx - флаги (активный уровень 0) разрешают измерения по осям ZYX (если отключить ось Z то отключится и измерение сопростивления r_hall) данные отключённой оси остаются в регистре данных, dr_polar - флаг определяет полярность активного логического уровня на выводе прерывания dtrd, int_latch - флаг захвата прерываний: «1» - прерывания исчезают только после чтения регистра 0x4A; «0» - прерывания исчезают при исчезновении обстоятельств их вызвавших, int_polar - флаг определяет полярность активного логического уровня на выводе прерывания int.
#define	REG_BMM_LOW_TH			0x4F		//	Граница низкопорогового  прерывания		|										low_threshold<7:0>										| 0x00	|	low_threshold<7:0> - граничное значение для низкопорогового прерывания
#define	REG_BMM_HIGH_TH			0x50		//	Граница высокопорогового прерывания		|										high_threshold<7:0>										| 0x00	|	high_threshold<7:0> - граничное значение для высокопорогового прерывания
#define	REG_BMM_REP_XY			0x51		//	Количество выборок для осей XY			|											rep_xy<7:0>											| 0x00	|	rep_xy<7:0>    - беззнаковое количество выборок для осей x/y. Количество выборок = 1 + 2 * значение регистра
#define	REG_BMM_REP_Z			0x52		//	Количество выборок для оси  Z 			|											rep_z<7:0>											| 0x00	|	rep_z<7:0>     - беззнаковое количество выборок для оси z. Количество выборок = 1 + значение регистра
#define REG_BMM_DIG_X1			0x5D		//	Значения корректировки					|											dig_x1<7:0>											| конст	|	dig_x1<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Y1			0x5E		//	Значения корректировки					|											dig_y1<7:0>											| конст	|	dig_y1<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z4_LSB		0x62		//	Значения корректировки					|											dig_z4<7:0>											| конст	|	dig_z4<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z4_MSB		0x63		//	Значения корректировки					|											dig_z4<15:8>										| конст	|	dig_z4<15:8>   - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_X2			0x64		//	Значения корректировки					|											dig_x2<7:0>											| конст	|	dig_x2<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Y2			0x65		//	Значения корректировки					|											dig_y2<7:0>											| конст	|	dig_y2<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z2_LSB		0x68		//	Значения корректировки					|											dig_z2<7:0>											| конст	|	dig_z2<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z2_MSB		0x69		//	Значения корректировки					|											dig_z2<15:8>										| конст	|	dig_z2<15:8>   - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z1_LSB		0x6A		//	Значения корректировки					|											dig_z1<7:0>											| конст	|	dig_z1<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z1_MSB		0x6B		//	Значения корректировки					|											dig_z1<15:8>										| конст	|	dig_z1<15:8>   - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_XYZ1_LSB	0x6C		//	Значения корректировки					|											dig_xyz1<7:0>										| конст	|	dig_xyz1<7:0>  - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_XYZ1_MSB	0x6D		//	Значения корректировки					|											dig_xyz1<15:8>										| конст	|	dig_xyz1<15:8> - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z3_LSB		0x6E		//	Значения корректировки					|											dig_z3<7:0>											| конст	|	dig_z3<7:0>    - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_Z3_MSB		0x6F		//	Значения корректировки					|											dig_z3<15:8>										| конст	|	dig_z3<15:8>   - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_XY2			0x70		//	Значения корректировки					|											dig_xy2<7:0>										| конст	|	dig_xy2<7:0>   - значение устанавливается на заводе изготовителе
#define REG_BMM_DIG_XY1			0x71		//	Значения корректировки					|											dig_xy1<7:0>										| конст	|	dig_xy1<7:0>   - значение устанавливается на заводе изготовителе

#include "iarduino_RTC_I2C.h"																									//	Подключаем файл iarduino_RTC_I2C.h - для работы с шиной I2C
																																//
class iarduino_Position_BMX055_BMM: public iarduino_Position_BMX055_BASE{														//
	public:																														//
	/**	функции доступные пользователю **/																						//
//		Инициализация датчика:																									//
		bool	begin(iarduino_I2C_Select* ptrI2C, bool setZero, float*ptrX,float*ptrY,float*ptrZ,float*ptrT, float*ptrQ1,float*ptrQ2,float*ptrQ3,float*ptrQ4 ){ // Аргументы: ptrI2C - указатель на объект работы с шиной I2C, setZero - неиспользуемый флаг, указатели на переменные выводимых данных)
					selI2C    = ptrI2C;																							//	Присваиваем указателю selI2C адрес из указателя ptrI2C
					axisX     = ptrX;																							//	Присваиваем указателю axisX  адрес из указателя ptrX который ссылается на переменную axisX класса iarduino_Position_BMX055 предназначенную для вывода данных по оси X
					axisY     = ptrY;																							//	Присваиваем указателю axisY  адрес из указателя ptrY который ссылается на переменную axisY класса iarduino_Position_BMX055 предназначенную для вывода данных по оси Y
					axisZ     = ptrZ;																							//	Присваиваем указателю axisZ  адрес из указателя ptrZ который ссылается на переменную axisZ класса iarduino_Position_BMX055 предназначенную для вывода данных по оси Z
					temp      = ptrT;																							//	Присваиваем указателю temp   адрес из указателя ptrT который ссылается на переменную temp  класса iarduino_Position_BMX055 предназначенную для вывода температуры
					uint8_t i = 0;																								//	Определяем переменную для подсчёта количества попыток выхода из режима приостановки (suspend) и сравнения ID чипа
					uint8_t j[2];																								//	Объявляем массив j размером 2 байта для чтения 16-битных корректировочных значений
				//	Выходим из режима приостановки (suspend) и устанавливаем рабочие параметры									//	Чип сам входит в режим приостановки (suspend) сразу после подачи питания. Переход в активный режим (Normal) нормальный или (Forced) принудительный, возможен только из спящего (sleep) режима.
					while (	selI2C->readByte(BMM_ADDRES, REG_BMM_CHIPID) != BMM_ID){											//	Если содержимое регистра REG_BMM_CHIPID не совпало с реальным ID чипа указанным в константе BMM_ID, то ...
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_0, 0x82);												//	Выполняем программную перезагрузку установив оба бита softreset<1:0> регистра REG_BMM_CTRL_0
							delay(100);																							//	Ждём сброса значений регистров
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_0, 0x01);												//	Выходим из режима приостановки (suspend) в спящий (Sleep) режим, установив бит pwr_control регистра REG_BMM_CTRL_0
							delay(100);																							//	Ждём выхода из режима приостановки (suspend)
							i++; if(i>10){return false;}																		//	Возвращаем ошибку если более 10 попыток чтения и сравнения ID увенчались провалом
					}																											//
					setBandwidths(varBandwidth);																				//	Устанавливаем пропускную способность магнитометра с переходом из спящего (Sleep) режима в активный, нормальный (Normal) режим работы
					delay(100);																									//	Ждём установки нормального (Normal) режима работы
					setScale(BMM_REGULAR);																						//	Устанавливаем количество выборок магнитометра как в обычном предустановленном режиме BMM_REGULAR
				//	Читаем корректировочные значения																			//
					dig_x1 =selI2C->readByte (BMM_ADDRES, REG_BMM_DIG_X1);														//	Читаем один байт корректировочных значений из регистра  REG_BMM_DIG_X1
					dig_x2 =selI2C->readByte (BMM_ADDRES, REG_BMM_DIG_X2);														//	Читаем один байт корректировочных значений из регистра  REG_BMM_DIG_X2
					dig_y1 =selI2C->readByte (BMM_ADDRES, REG_BMM_DIG_Y1);														//	Читаем один байт корректировочных значений из регистра  REG_BMM_DIG_Y1
					dig_y2 =selI2C->readByte (BMM_ADDRES, REG_BMM_DIG_Y2);														//	Читаем один байт корректировочных значений из регистра  REG_BMM_DIG_Y2
					dig_xy1=selI2C->readByte (BMM_ADDRES, REG_BMM_DIG_XY1);														//	Читаем один байт корректировочных значений из регистра  REG_BMM_DIG_XY1
					dig_xy2=selI2C->readByte (BMM_ADDRES, REG_BMM_DIG_XY2);														//	Читаем один байт корректировочных значений из регистра  REG_BMM_DIG_XY2
							selI2C->readBytes(BMM_ADDRES, REG_BMM_DIG_Z1_LSB,   j, 2);	dig_z1   = ((uint16_t)j[1]<<8) | j[0];	//	Читаем два байта корректировочных значений из регистров REG_BMM_DIG_Z1_LSB и REG_BMM_DIG_Z1_MSB
							selI2C->readBytes(BMM_ADDRES, REG_BMM_DIG_Z2_LSB,   j, 2);	dig_z2   = (( int16_t)j[1]<<8) | j[0];	//	Читаем два байта корректировочных значений из регистров REG_BMM_DIG_Z2_LSB и REG_BMM_DIG_Z2_MSB
							selI2C->readBytes(BMM_ADDRES, REG_BMM_DIG_Z3_LSB,   j, 2);	dig_z3   = (( int16_t)j[1]<<8) | j[0];	//	Читаем два байта корректировочных значений из регистров REG_BMM_DIG_Z3_LSB и REG_BMM_DIG_Z3_MSB
							selI2C->readBytes(BMM_ADDRES, REG_BMM_DIG_Z4_LSB,   j, 2);	dig_z4   = (( int16_t)j[1]<<8) | j[0];	//	Читаем два байта корректировочных значений из регистров REG_BMM_DIG_Z4_LSB и REG_BMM_DIG_Z4_MSB
							selI2C->readBytes(BMM_ADDRES, REG_BMM_DIG_XYZ1_LSB, j, 2);	dig_xyz1 = ((uint16_t)j[1]<<8) | j[0];	//	Читаем два байта корректировочных значений из регистров REG_BMM_DIG_XYZ1_LSB и REG_BMM_DIG_XYZ1_MSB
					return true;																								//
		}																														//
																																//
//		Самотестирование датчика:																								//
		uint8_t	test(void){																										//	Аргумент: отсутствует
					uint8_t i, j;																								//	Объявляем переменные для хранения значений однобайтовых регистров
					float   k[2];																								//	Объявляем массив для хранения показаний по оси Z при подаче положительного и отрицательного тока на втроенную катушку индуктивности
				//	Обычное самотестирование (Normal Self Test):																//	Проверяется наличие соединения и КЗ тракта FlipCore с ASIC для осей X и Y. Проверяется наличие и скорость их обработки сигналов по всем осям, а уровни сигналов сравниваются с пороговыми значениями.
					if	  (	selI2C->readByte (BMM_ADDRES, REG_BMM_CHIPID) != BMM_ID){return BMM_ERR_ID;}						//	Возвращаем ошибку если содержимое регистра REG_BMM_CHIPID не совпало с реальным ID чипа указанным в константе BMM_ID
					i = 	selI2C->readByte (BMM_ADDRES, REG_BMM_CTRL_1);														//	Читаем значение регистра REG_BMM_CTRL_1 в переменную i
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i|0x07);												//	Выполняем обычное самотестирование в спящем режиме (сохраняем значение переменной i обратно в регистр REG_BMM_CTRL_1 предварительно установив биты op_mode<1:0> и self_test
					while (	selI2C->readByte (BMM_ADDRES, REG_BMM_CTRL_1) & 0x01){;}											//	Ждём сброса бита self_test регистра REG_BMM_CTRL_1
					if	  (!selI2C->readByte (BMM_ADDRES, REG_BMM_X_LSB ) & 0x01){return BMM_ERR_ST;}							//	Если флаг x_self_test регистра REG_BMM_X_LSB не установлен, значит в результате самотестирования выявлены ошибки для канала оси X
					if	  (!selI2C->readByte (BMM_ADDRES, REG_BMM_Y_LSB ) & 0x01){return BMM_ERR_ST;}							//	Если флаг y_self_test регистра REG_BMM_Y_LSB не установлен, значит в результате самотестирования выявлены ошибки для канала оси Y
					if	  (!selI2C->readByte (BMM_ADDRES, REG_BMM_Z_LSB ) & 0x01){return BMM_ERR_ST;}							//	Если флаг z_self_test регистра REG_BMM_Z_LSB не установлен, значит в результате самотестирования выявлены ошибки для канала оси Z
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i);													//	Возврашаем исходные значения регистру REG_BMM_CTRL_1
				//	Расширенное самотестирование (Advanced Self Test):															//	Проверяются значения по оси Z на которую будет действовать магнитное поле создаваемое током проходящим через встроенную катушку индуктивности. Ток проходящий через катушку создаёт индукцию магнитного поля в ±100 мкТл (знак зависит от направления тока), значит разница двух показаний должна составлять около 200 мкТл.
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i|0x06);												//	Переходим в спящий режим (сохраняем значение переменной i обратно в регистр REG_BMM_CTRL_1 предварительно установив биты op_mode<1:0>
					j = 	selI2C->readByte (BMM_ADDRES, REG_BMM_INT_EN_1);													//	Читаем значение регистра REG_BMM_INT_EN_1 в переменную j
							selI2C->writeByte(BMM_ADDRES, REG_BMM_INT_EN_1, j|0x18);											//	Отключаем каналы осей X и Y (сохраняем значение переменной j обратно в регистр REG_BMM_INT_EN_1 предварительно установив биты channel_y и channel_x (0-включает канал, 1-отключает канал))
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i&0x39|0xC2);											//	Выполняем принудительное чтение данных с подачей положительного тока на катушку индуктивности (сохраняем значение переменной i обратно в регистр REG_BMM_CTRL_1 предварительно записав значения «11» в биты advanced_self_test<1:0> и «01» в биты op_mode<1:0>)
					while ((selI2C->readByte (BMM_ADDRES, REG_BMM_CTRL_1) & 0x06) != 0x06){;}									//	Ждём входа в спящий режим (будут установлены биты op_mode<1:0>), модуль сам переходит в спящий режим после выполнения одного считывания в принудительном режиме
							readADC(); k[0] = (float) mag_adc[2] * varQuantum;													//	Читаем показания АЦП магнитометра в массив mag_adc. Преобразуем показание оси Z в мГс и сохраняем его в 0 ячейке массива k
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i&0x39|0x82);											//	Выполняем принудительное чтение данных с подачей отрицательного тока на катушку индуктивности (сохраняем значение переменной i обратно в регистр REG_BMM_CTRL_1 предварительно записав значения «10» в биты advanced_self_test<1:0> и «01» в биты op_mode<1:0>)
					while ((selI2C->readByte (BMM_ADDRES, REG_BMM_CTRL_1) & 0x06) != 0x06){;}									//	Ждём входа в спящий режим (будут установлены биты op_mode<1:0>), модуль сам переходит в спящий режим после выполнения одного считывания в принудительном режиме
							readADC(); k[1] = (float) mag_adc[2] * varQuantum;													//	Читаем показания АЦП магнитометра в массив mag_adc. Преобразуем показание оси Z в мГс и сохраняем его в 1 ячейке массива k
							selI2C->writeByte(BMM_ADDRES, REG_BMM_INT_EN_1, j);													//	Возврашаем исходные значения регистру REG_BMM_INT_EN_1 из переменной j
							selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i);													//	Возврашаем исходные значения регистру REG_BMM_CTRL_1 из переменной i
				//	Катушка создаёт магнитную индукцию в 1000 мГс, разница между +1000 и -1000 мГс должна быть равна 2000 мГс:	//
                    if	  ((k[0]-k[1]) > 2200.0f){return BMM_ERR_ST;}															//	Если разница между двумя значениями по оси Z больше 220 мкТл (2200 мГс) значит тест провалился. Встроенная катушка создаёт магнитную индукцию в 100 мкТл при прямом токе и -100 мкТл при обратном токе, значит разница в показаниях должна составлять 200 мкТл (2000 мГс)
                    if	  ((k[0]-k[1]) < 1800.0f){return BMM_ERR_ST;}															//	Если разница между двумя значениями по оси Z меньше 180 мкТл (1800 мГс) значит тест провалился. Встроенная катушка создаёт магнитную индукцию в 100 мкТл при прямом токе и -100 мкТл при обратном токе, значит разница в показаниях должна составлять 200 мкТл (2000 мГс)
					return 0;																									//	Выводим результат успешного самотестирования
		}																														//
																																//
//		Чтение данных из датчика:																								//
		bool	read(uint8_t thisMera){																							//	Аргумент: мера измерения читаемых данных (BMM_MG / BMM_MCT)
					if(!readADC()){return false;}																				//	Читаем показания АЦП магнитометра в массив mag_adc
					float j = 1;																								//	Определяем множитель
					switch(thisMera){																							//	В зависимости от установленной меры измерений
						default:		thisMera=BMM_MG;																		//	По умолчанию значения возвращаются в мГс
						case BMM_MG:	j=1.0f; break;																			//	Если данные требуется возвращать в мГс
						case BMM_MCT:	j=0.1f; break;																			//	Если данные требуется возвращать в мкТл
					}																											//	Результирующее значение получается из скорректированных данных АЦП (mag_adc) умноженных на шаг квантования (varQuantum) с вычетом вычисленного смещения (magBias) и преобразованного в требуемую величину измерений
																																//
					*axisX = -(((float) mag_adc[1] * varQuantum - magBias[1]) * j);												//  Получаем значение для оси X            | Ось Z                  На картинке изображены:
					*axisY =   ((float) mag_adc[0] * varQuantum - magBias[0]) * j ;												//  Получаем значение для оси Y            |                        Ось X, Ось Y, Ось Z - оси модуля
					*axisZ =   ((float) mag_adc[2] * varQuantum - magBias[2]) * j ;												//  Получаем значение для оси Z            |       / Ось X          AX, AY, AZ          - оси акселерометра
					*temp  =    (float) mag_adc[3] * 273.15 / 10000;															//  Получаем значение температуры          |AZ    /                 GX, GY, GZ          - оси гироскопа
					return true;																								//                                         |GZ   / AX               MX, MY, MZ          - оси магнитометра
		}		//	значение для оси X находится в ячейках 0 массивов mag_adc и magBias											//                                         |MZ  / GX                
				//	значение для оси Y находится в ячейках 1 массивов mag_adc и magBias											//                                         |   /-MY                 Ось X магнитометра находится на оси Y модуля
				//	значение для оси Z находится в ячейках 2 массивов mag_adc и magBias											//                                         |  /                     Ось Y магнитометра направлена в обратную сторону относительно оси X модуля
				//	значение для температуры находится в 3 ячейке массива mag_adc												//                   AY         *----------|-/------------*         Ось Z магнитометра совпадает с осью Z модуля
				//	Но так как у магнитометра только ось Z совпадает с акселерометром и гироскопом								//   _______________GY_________/___________|/            /|         Если на картинке повернуть магнитометр на 90° по часовой стрелке, то
				//	а по осям XY магнитометр сдвинут на 90° против часовой стрелке, то ...										//   Ось Y         MX         /                         / *         Оси магнитометра совпадут с осями остальных датчиков и модуля
				//	По указателю axisX мы передаём значение оси -Y магнитометра													//                           *-------------------------* /          Оси героскопа направлены не вдоль осей модуля,
				//	По указателю axisY мы передаём значение оси  X магнитометра													//                           |                         |/           а вращаются по часовой стрелке в направлении этих осей,
				//	По указателю axisZ мы передаём значение оси  Z магнитометра													//                           *-------------------------*            так как это нарисовано на плате Trema-модуля IMU - 9 DOF
																																//
//		Установка количества выборок магнитометра:																				//
		void	setScale(uint8_t scale){																						//	Аргумент: предустановленный режим
					uint8_t rep_xy, rep_z, data_rate;																			//	Объявляем переменные для хранения количества выборок по осям XY, Z и требуемой для них полосы пропускания.
					switch (scale){																								//	Проверяем значение аргумента функции (1 из 4 возможных предустановленных режимов работы магнитометра)
						default:		  	scale=BMM_REGULAR;																	//	Если значение некорректное, то меняем его на BMM_REGULAR (обычный предустановленный режим)
						case BMM_LOW_PWR:	rep_xy=3;				rep_z=3;		data_rate=BMM_10Hz; break;					//	Экономичный предустановленный режим     -  3 выборки для осей XY,  3 выборки для оси Z, рекомендуемая полоса пропускания 10 Гц, шумы по осям X/Y/Z = 1.0/1.0/1.4 мкТл, среднее потребление тока 0.17 мА.
						case BMM_REGULAR:	rep_xy=9;				rep_z=15;		data_rate=BMM_10Hz; break;					//	Обычный     прдуустановленный режим     -  9 выборок для осей XY, 15 выборок для оси Z, рекомендуемая полоса пропускания 10 Гц, шумы по осям X/Y/Z = 0.6/0.6/0.6 мкТл, среднее потребление тока 0.50 мА.
						case BMM_ENHANCED:	rep_xy=15;				rep_z=27;		data_rate=BMM_10Hz; break;					//	Улучшенный  прдуустановленный режим     - 15 выборок для осей XY, 27 выборок для оси Z, рекомендуемая полоса пропускания 10 Гц, шумы по осям X/Y/Z = 0.5/0.5/0.5 мкТл, среднее потребление тока 0.80 мА.
						case BMM_HIGH:		rep_xy=47;				rep_z=83;		data_rate=BMM_20Hz; break;					//	Прдустановленный режим высокой точности - 47 выборок для осей XY, 83 выборки для оси Z, рекомендуемая полоса пропускания 20 Гц, шумы по осям X/Y/Z = 0.3/0.3/0.3 мкТл, среднее потребление тока 4.90 мА.
					}						rep_xy=(rep_xy-1)/2;	rep_z=rep_z-1;												//	Количество выборок XY=1+2*rep_xy => rep_xy=(количество выборокXY-1)/2. Количество выборок Z=1+rep_z => rep_x=количество выборокZ-1.
					setBandwidths(data_rate); 																					//	Устанавливаем пропускную способность магнитометра с переходом в нормальный режим работы
					selI2C->writeByte(BMM_ADDRES, REG_BMM_REP_XY, rep_xy);														//	Устанавливаем количество выборок по осям XY
					selI2C->writeByte(BMM_ADDRES, REG_BMM_REP_Z , rep_z );														//	Устанавливаем количество выборок по оси Z
		}																														//
																																//
//		Установка полосы пропускания для фильтрованных данных:																	//
		void	setBandwidths(uint8_t bandwidths){																				//	Аргумент: полоса пропускания
					uint8_t i=0;																								//	Значение для записи в регистр REG_BMM_CTRL_1.
					switch (bandwidths){																						//	Проверяем значение аргумента функции (максимальное детектируемое угловое ускорение)
						default:		bandwidths=BMM_10Hz;																	//	Если значение некорректное, то меняем его на BMM_10Hz (значение по умолчанию).
						case BMM_10Hz:	i=0x00; break;																			//	Для полосы пропускания 10 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_2Hz:	i=0x01; break;																			//	Для полосы пропускания  2 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_6Hz:	i=0x02; break;																			//	Для полосы пропускания  6 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_8Hz:	i=0x03; break;																			//	Для полосы пропускания  8 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_15Hz:	i=0x04; break;																			//	Для полосы пропускания 15 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_20Hz:	i=0x05; break;																			//	Для полосы пропускания 20 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_25Hz:	i=0x06; break;																			//	Для полосы пропускания 25 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
						case BMM_30Hz:	i=0x07; break;																			//	Для полосы пропускания 30 Гц нужно записать i в биты data_rate<2:0> регистра REG_BMM_CTRL_1.
					}																											//
					varBandwidth=bandwidths;																					//	Запоминаем выбранную полосу пропускания
					selI2C->writeByte(BMM_ADDRES, REG_BMM_CTRL_1, i<<3);														//	Устанавливаем пропускную способность магнитометра (биты data_rate<2:0> регистра REG_BMM_CTRL_1)
		}																														//
																																//
//		Выполнение компенсации смещения данных:																					//	Усреднённые значения на момент выполнения компенсации заносятся в массив magBias и потом будут вычитаться из реальных показаний
		void	setFastOffset(uint8_t offset){																					//	Аргумент: BMM_RESET - сброс накопленных показаний, BMM - накопление показаний смещения.
					static int16_t mag_max[3] = {-32768,-32768,-32768};															//	Определяем массив для хранения максимальных показаний АЦП по осям XYZ
					static int16_t mag_min[3] = { 32767, 32767, 32767};															//	Определяем массив для хранения минимальных  показаний АЦП по осям XYZ
					if( readADC() ){																							//	Читаем АЦП.
					//	Накапливаем данные для определения лимитов:																//
						for(uint8_t i=0; i<3; i++){																				//	Проходим по данным трёх осей (X/Y/Z)
							if( mag_max[i]<mag_adc[i] ){ mag_max[i]=mag_adc[i]; }												//	Определяем максимум для оси mag_max[i] из текущих прочитанных данных оси mag_adc[i].
							if( mag_min[i]>mag_adc[i] ){ mag_min[i]=mag_adc[i]; }												//	Определяем минимум  для оси mag_min[i] из текущих прочитанных данных оси mag_adc[i].
						}																										//
					//	Определяем смещение показаний:																			//
						magBias[0] = float((int32_t)(mag_max[0]+mag_min[0])/2) * varQuantum;									//	среднее значение показаний АЦП по оси X умножаем на шаг квантования varQuantum и получаем смещение показаний оси в мГс
						magBias[1] = float((int32_t)(mag_max[1]+mag_min[1])/2) * varQuantum;									//	среднее значение показаний АЦП по оси Y умножаем на шаг квантования varQuantum и получаем смещение показаний оси в мГс
						magBias[2] = float((int32_t)(mag_max[2]+mag_min[2])/2) * varQuantum;									//	среднее значение показаний АЦП по оси Z умножаем на шаг квантования varQuantum и получаем смещение показаний оси в мГс
					}																											//
				//	Сбрасываем накопленные значения:																			//
					if( offset==BMM_RESET ){																					//
						magBias[0]=     0; magBias[1]=     0; magBias[2]=     0;												//
						mag_max[0]=-32768; mag_max[1]=-32768; mag_max[2]=-32768;												//
						mag_min[0]= 32768; mag_min[1]= 32768; mag_min[2]= 32768;												//
					}																											//
		}																														//
																																//
//		Установка массива компенсации смещения данных:																			//	Вместо выполнения компенсайии смещения данных можно просто указать массив с результатами.
		void	setFastOffset(float* offset){ magBias[0]=offset[0]; magBias[1]=offset[1]; magBias[2]=offset[2]; }				//	Аргумент: указатель на массив с данными.
																																//
//		Получение массива компенсации смещения данных:																			//	После выполнения компенсайии смещения данных можно получить массив с результатами.
		void	getFastOffset(float* offset){ offset[0]=magBias[0]; offset[1]=magBias[1]; offset[2]=magBias[2]; }				//	Аргумент: указатель на массив для данных.
																																//
//		Функции не используемые магнитометром:																					//
		uint8_t	getFilter(void){return 0;}																						//	Фильтр используется только для объекта BMX, но функцию объявить нужно, так как данный класс является производным и должен содержать все функции полиморфного класса
																																//
	private:																													//
	/**	Внутренние переменные **/																								//
		int8_t		dig_x1;																										//	Объявляем переменную для хранеия корректировочного значения
		int8_t		dig_y1;																										//	Объявляем переменную для хранеия корректировочного значения
		int8_t		dig_x2;																										//	Объявляем переменную для хранеия корректировочного значения
		int8_t		dig_y2;																										//	Объявляем переменную для хранеия корректировочного значения
		uint16_t	dig_z1;																										//	Объявляем переменную для хранеия корректировочного значения
		int16_t		dig_z2;																										//	Объявляем переменную для хранеия корректировочного значения
		int16_t		dig_z3;																										//	Объявляем переменную для хранеия корректировочного значения
		int16_t		dig_z4;																										//	Объявляем переменную для хранеия корректировочного значения
		uint8_t		dig_xy1;																									//	Объявляем переменную для хранеия корректировочного значения
		int8_t		dig_xy2;																									//	Объявляем переменную для хранеия корректировочного значения
		uint16_t	dig_xyz1;																									//	Объявляем переменную для хранеия корректировочного значения
		int16_t		mag_adc[4]={0,0,0,0};																						//	Определяем массив для хранения прочитанных показаний АЦП по осям XYZ и температуры
		float		magBias[3]={0,0,0};																							//	Определяем массив для хранения смещения показаний магнитометра в мГс для осей XYZ на момент выполнения компенсации смещения данных
																																//
//		Чтение показаний АЦП магнитометра с корректировкой прочитанных значений:												//
		bool	readADC(void){																									//	Аргумент: отсутствует
					uint8_t i[8]; if( !selI2C->readBytes(BMM_ADDRES, REG_BMM_X_LSB, i, 8) ){ return false; }					//	Читаем 8 байт регистров АЦП начиная с регистра REG_BMM_X_LSB в объявленный массив i
					uint16_t j = ((uint16_t(i[7])<<8) | i[6]) >> 2;																//	Получаем сопростивление по которому будет рассчитана температура
					if(i[6] & 0x01 == 0){return false;}																			//	Если не установлен бит data_ready регистра REG_BMM_TEMP_LSB, значит данные не новые
			/* T */	mag_adc[3] = int16_t(uint16_t((int32_t(dig_xyz1)<<14)/(j != 0 ? j : dig_xyz1))-uint16_t(0x4000));			//	Рассчитываем температуру по полученному сопротивлению
			/* X */ mag_adc[0] = int16_t((int32_t (((int16_t(i[1])<<8)|i[0])>>3)*((((((int32_t(dig_xy2)*((int32_t(mag_adc[3])*int32_t(mag_adc[3]))>>7))+(int32_t(mag_adc[3])*int32_t(int16_t(dig_xy1)<<7)))>>9)+int32_t(0x100000))*int32_t(int16_t(dig_x2)+int16_t(0xA0)))>>12))>>13)+(int16_t(dig_x1)<<3);	// Получаем значение АЦП для оси X и корректируем его
			/* Y */ mag_adc[1] = int16_t((int32_t (((int16_t(i[3])<<8)|i[2])>>3)*((((((int32_t(dig_xy2)*((int32_t(mag_adc[3])*int32_t(mag_adc[3]))>>7))+(int32_t(mag_adc[3])*int32_t(int16_t(dig_xy1)<<7)))>>9)+int32_t(0x100000))*int32_t(int16_t(dig_y2)+int16_t(0xA0)))>>12))>>13)+(int16_t(dig_y1)<<3);	// Получаем значение АЦП для оси Y и корректируем его
			/* Z */ mag_adc[2] =        ((int32_t((((int16_t(i[5])<<8)|i[4])>>1)-dig_z4)<<15)-((int32_t(dig_z3)*int32_t(int16_t(j)-int16_t(dig_xyz1)))>>2))/(dig_z2+int16_t(((int32_t(dig_z1)*(int16_t(j)<<1))+(1<<15))>>16));																				// Получаем значение АЦП для оси Z и корректируем его
					return true;																								//
		}																														//
																																//
	/**	Внутренние переменные **/																								//
		iarduino_I2C_Select *selI2C;																							//	Создаём указатель на объект работы с шиной I2C.
//		uint8_t	varRanges =		±256 мкТл для осей XY / ±1024 мкТл для оси Z			(1 мкТл = 10 мГс)						//	Определяем переменную для хранения полного диапазона измерений (какой индукции магнитного поля соответствует значение где все биты установлены в «1»)
		uint8_t	varBandwidth =	BMM_10Hz;																						//	Определяем переменную для хранения полосы пропускания фильтрованных данных
		float	varQuantum =	1.0/1.6;	/***  1.0/1.6 мГс  = 1.0/16.0 мкТл  ***/											//	Определяем переменную для хранения шага квантования (какой индукции магнитного поля мГс равно значение «1» из регистра данных)
		float *	axisX;																											//	Объявляем указатель который будет ссылаться на переменную для вывода данных по оси X
		float *	axisY;																											//	Объявляем указатель который будет ссылаться на переменную для вывода данных по оси Y
		float *	axisZ;																											//	Объявляем указатель который будет ссылаться на переменную для вывода данных по оси Z
		float *	temp;																											//	Объявляем указатель который будет ссылаться на переменную для вывода температуры
};																																//
																																//
#endif																															//