Управление офисным освещением по Wi-Fi. Часть 2: Сенсорная технология Q-touch

от автора

Продолжаем наш амбициозный «Hello, World!» на отладочной плате Atmel SAMD21 Xplained, затеянный в первой части, в которой была описана работа с Wi-Fi модулем WINC1500.
Сегодня будет продемонстрирован пример обработки сенсорных кнопок и слайдера при помощи библиотеки Q-touch.

В третьей части цикла, как и было обещано, данные с этих сенсоров будут «запаковываться» в посылку ModBus TCP и передаваться по Wi-Fi в систему управления освещением в нашем офисе.

Для начала, разберемся что же это за Q-touch. Это атмеловская реализация технологии обработки сенсорных резистивных кнопок и слайдеров, сопровождаемая библиотекой для упрощения работы с ними. Причем во всех микроконтроллерах SAMD реализован аппаратный контроллер Q-touch (так называемый периферийный контроллер прикосновений (PTC)). Он позволяет как минимизировать число использованных выводов микроконтроллера, так и нагрузку на вычислительное ядро.
В качестве сенсоров будем использовать модуль расширения ATQT1-XPRO, который, как уже упоминалось в прошлой статье, может быть установлен на любую отладочную плату из серии Xplained Pro.
Технология Qtouch поддерживает следующие виды сенсоров: кнопки, слайдеры, роторы и определение приближения (proximity).

Технологии Qtouch и QMatrix

QTouch основана на измерении собственной емкости, а QMatrix на измерении совместной.
Измерение с использованием собственной емкости подразумевает заряд чувствительного электрода неизвестной емкости до известного потенциала. Результирующий заряд передается в измерительную цепь. С помощью циклов заряда-и-передачи можно измерить емкость чувствительной пластины.
Измерение с использованием совместной емкости осуществляется с помощью двух электродов. Один из электродов выступает в качестве эмиттера, который принимает заряд, который передается логическими импульсами в последовательном (burst) режиме. Второй электрод выступает как получатель, который связывается с эмиттером через диэлектрик, из которого сделана тач-панель. Когда палец касается панели, совместное поле уменьшается, и прикосновение определяется.

Qtouch QMatrix
Собственная емкость Совместная емкость
Надежный и простой дизайн электродов Хорошо определенная область детектирования нажатия
Идеальна для небольшого количества сенсоров Идеально для большого количества сенсоров (больше 10)
Хорошее определение приближения, на большем расстоянии Хорошо приспосабливающаяся к влажности и окружающей среде
Теоретически возможна любая форма электрода Пассивное отслеживание – возможны более длинные пути
Легко настраивается чувствительность Хорошо приспосабливающаяся к шуму и наводкам по земле

В контроллерах серии SAMD20 и 21 технология QTouch/QMatrix встроена. Специальный блок, который за нее отвечает – это периферийный контроллер прикосновений (PTC). Схема работы всей системы показана на рисунке ниже.

Создаем проект

Для освоения новой периферии очень удобно пользоваться проектами-примерами. А для QTouch есть еще специальный плагин QTouch Composer, который делает разработку визуальной. Но если необходимо встроить сенсорные кнопки в уже существующий проект, надо понимать всю последовательность действий и настройки. Сейчас этим и займемся.
Общая схема работы библиотеки приведена на блок схеме:

Добавляем в проект с помощью визарда PTC и RTC.

Конфигурация настраивается в файле touch_config_samd.h. Пройдемся по основным параметрам.
Сначала необходимо выбрать способ определения прикосновений: собственная емкость или совместная. Выбор осуществляется с помощью задания значений соответствующих констант.

#define DEF_TOUCH_MUTLCAP               (1u) #define DEF_TOUCH_SELFCAP               (0u) 

Приоритет прерываний от PTC контроллера о завершении преобразования может иметь значения от 0 до 3 (0 самый высокий приоритет). И устанавливается с помощью дефайна:

#define DEF_TOUCH_PTC_ISR_LVL   (1u) 

Как уже упоминалось выше, для определения прикосновения с помощью совместной емкости необходимо две линии: X и Y. У samd21 16 линий X и Y. В данном случае (использования отладки с платой расширения) у нас нет выбора на какую пару линий какую кнопку/слайдер/ротор заводить. Порядок указания пар выводов задает номера каналов. Для ротора/слайдера обязательно использовать одну и ту же линию Y для всех каналов. Указание линий осуществляется с помощью соответствующего дефайна:

#define DEF_MUTLCAP_NODES  X(8), Y(10), X(9), Y(10), X(2), Y(12), X(3), Y(12), \ 	X(8), Y(12), X(9), Y(12), X(2), Y(13), X(3), Y(13), \ 	X(8), Y(13), X(9), Y(13) 

Указываем количество каналов (для кнопки всегда 1 канал, для ротора/слайдера от 3 до 8). В нашем случае используется 2 кнопки и по 4 канала на ротор и слайдер, всего 10 каналов:

#define DEF_MUTLCAP_NUM_CHANNELS   (10) /* Total number of channels */ 

Указываем количество сенсоров (у нас 2 кнопки, один слайдер и один ротор итого 4):

#define DEF_MUTLCAP_NUM_SENSORS  (4)  /* Total number of sensors */ 

Указываем количество роторов/сенсоров (у нас один слайдер и один ротор итого 2):

#define DEF_MUTLCAP_NUM_ROTORS_SLIDERS  (2)  /* Number of rotor sliders */ 

Переходим к указанию параметров преобразования.
Уровень фильтрации влияет на точность и скорость преобразования. Чем выше уровень (от 1 до 64), тем больше семплов приходится на 1 преобразование, что улучшает соотношение шум/сигнал, но увеличивает время преобразования.

#define DEF_MUTLCAP_FILTER_LEVEL  FILTER_LEVEL_32            /* Filter level */ 

Усиление сигнала с сенсоров настраивается поканально. Диапазон значений от 1 до 32.

#define DEF_MUTLCAP_GAIN_PER_NODE  GAIN_1, GAIN_1, GAIN_1, GAIN_1, GAIN_1, \ 	GAIN_1, GAIN_1, GAIN_1, GAIN_1, GAIN_1 

Устанавливаем период опроса в миллисекундах.

#define DEF_TOUCH_MEASUREMENT_PERIOD_MS 20u 

Как и для механических кнопок, для сенсорных предусмотрен своеобразный антидребезг. Он заключается в том, что вы указываете в течении скольки циклов измерения уровень сигнала должен превышать пороговый для детектирования прикосновения к кнопке/ротору/слайдеру.

#define   DEF_MUTLCAP_DI         4u 

Бывает так, что какой-то предмет долго касается сенсора. В таком случае необходимо через некоторое время перекалибровать сенсор с учетом новых условий работы. Для установки времени, через которое происходит перекалибровка, используется специальная константа. Время устанавливается в единицах 200 мс (т.е. значение 5 соответствует 1 сек). Если время установить в 0, то автоматическая рекалибровка проводиться не будет.

#define   DEF_MUTLCAP_MAX_ON_DURATION       0u 

Можно разрешить или запретить вывод отладочный информации для Qtouch Analyzer:

<cut />#define DEF_TOUCH_QDEBUG_ENABLE 0u 

По каким-то своим странным соображениям Atmel не включил стандартных функций инициализации PTC (как с другой периферией) и определение нескольких необходимых констант. Поэтому это все надо делать самостоятельно. Чем мы сейчас и займемся.
Прежде всего нам необходимо инициализировать RTC, так как по прерываниям от него будут проверяться срабатывания кнопок в нашем случае. Настраиваем RTC, регистрируем callback, пишем код для callback. RTC будет генерировать прерывания раз в 1 мсек, если прошло столько мсек, сколько у нас интервал между считыванием кнопок, то выставляем соответствующий флаг, который будет проверяться в main.
Необходимые объявления:

Функции работы с таймером

// RTC Interrupt timing definition #define TIME_PERIOD_1MSEC 33u  /* ! QTouch Library Timing info. */ touch_time_t touch_time; volatile uint16_t touch_time_counter = 0u; struct rtc_module rtc_instance; Необходимые функции: void rtc_overflow_callback(void) { 	/* Do something on RTC overflow here */ 	if(touch_time_counter == touch_time.measurement_period_ms) 	{ 		touch_time.time_to_measure_touch = 1u; 		touch_time.current_time_ms = touch_time.current_time_ms + 		touch_time.measurement_period_ms; 		touch_time_counter = 0u; 	} 	else 	{ 		touch_time_counter++; 	} } void configure_rtc_callbacks(void) { 	/* register callback */ 	rtc_count_register_callback(&rtc_instance,	rtc_overflow_callback, RTC_COUNT_CALLBACK_OVERFLOW); 	/* Enable callback */ 	rtc_count_enable_callback(&rtc_instance,RTC_COUNT_CALLBACK_OVERFLOW); } void configure_rtc_count(void) { 	struct rtc_count_config config_rtc_count; 	rtc_count_get_config_defaults(&config_rtc_count);  	config_rtc_count.prescaler           = RTC_COUNT_PRESCALER_DIV_1; 	config_rtc_count.mode                = RTC_COUNT_MODE_16BIT; 	config_rtc_count.continuously_update = true; 	/* initialize rtc */ 	rtc_count_init(&rtc_instance,RTC,&config_rtc_count);  	/* enable rtc */ 	rtc_count_enable(&rtc_instance); } void timer_init(void) { 	/* Configure and enable RTC */ 	configure_rtc_count();  	/* Configure and enable callback */ 	configure_rtc_callbacks();  	/* Set Timer Period */ 	rtc_count_set_period(&rtc_instance,TIME_PERIOD_1MSEC); } 

Теперь нужно настроить сам PTC. Сначала добавляем необходимые структуры:

Структуры для PTC

static touch_mutlcap_config_t mutlcap_config = { 	DEF_MUTLCAP_NUM_CHANNELS,       /* Mutual Cap number of channels. */ 	DEF_MUTLCAP_NUM_SENSORS,        /* Mutual Cap number of sensors. */ 	DEF_MUTLCAP_NUM_ROTORS_SLIDERS, /* Mutual Cap number of rotors and sliders. */  	/* Mutual Cap GLOBAL SENSOR CONFIGURATION INFO. */ 	{ 		DEF_MUTLCAP_DI,         /* uint8_t  di; Sensor detect integration (DI) limit. */ 		/* Interchanging Negative and Positive Drift rate, since Signal increases on Touch. */ 		DEF_MUTLCAP_ATCH_DRIFT_RATE, /* uint8_t  neg_drift_rate; Sensor negative drift rate. */ 		DEF_MUTLCAP_TCH_DRIFT_RATE, /* uint8_t  pos_drift_rate; Sensor positive drift rate. */ 		DEF_MUTLCAP_MAX_ON_DURATION, /* uint8_t  max_on_duration; Sensor maximum on duration. */ 		DEF_MUTLCAP_DRIFT_HOLD_TIME, /* uint8_t  drift_hold_time; Sensor drift hold time. */ 		DEF_MUTLCAP_ATCH_RECAL_DELAY,   /* uint8_t  pos_recal_delay; Sensor positive recalibration delay. */                 DEF_MUTLCAP_CAL_SEQ1_COUNT,                 DEF_MUTLCAP_CAL_SEQ2_COUNT, 		DEF_MUTLCAP_ATCH_RECAL_THRESHOLD, /* recal_threshold_t recal_threshold; Sensor recalibration threshold. */ 	},         { 	mutlcap_gain_per_node,          /* Mutual Cap channel gain setting. */ 	DEF_MUTLCAP_FREQ_MODE, /* Mutual Cap noise counter measure enable/disable. */         DEF_MUTLCAP_CLK_PRESCALE,         DEF_MUTLCAP_SENSE_RESISTOR,         DEF_MUTLCAP_CC_CAL_CLK_PRESCALE,         DEF_MUTLCAP_CC_CAL_SENSE_RESISTOR,         mutlcap_freq_hops, 	DEF_MUTLCAP_FILTER_LEVEL,       /* Mutual Cap filter level setting. */ 	DEF_MUTLCAP_AUTO_OS,            /* Mutual Cap auto oversamples setting.*/	                                       }, 	mutlcap_data_blk,               /* Mutual Cap data block index. */ 	PRIV_MUTLCAP_DATA_BLK_SIZE, /* Mutual Cap data block size. */ 	mutlcap_xy_nodes,                       /* Mutual Cap channel nodes. */         DEF_MUTLCAP_QUICK_REBURST_ENABLE, 	DEF_MUTLCAP_FILTER_CALLBACK     /* Mutual Cap filter callback function pointer. */ };  touch_config_t touch_config = { 	&mutlcap_config,                /* Pointer to Mutual Cap configuration structure. */ 	NULL, 	DEF_TOUCH_PTC_ISR_LVL,          /* PTC interrupt level. */ }; 

Макросы:

#define GET_MUTLCAP_SENSOR_STATE(SENSOR_NUMBER) p_mutlcap_measure_data-> \ 	p_sensor_states[(SENSOR_NUMBER / \ 	8)] & (1 << (SENSOR_NUMBER % 8)) 

Дефайны:

#define   DEF_MUTLCAP_CAL_SEQ1_COUNT            8 #define   DEF_MUTLCAP_CAL_SEQ2_COUNT            4 #define   DEF_MUTLCAP_CC_CAL_CLK_PRESCALE       PRSC_DIV_SEL_8 #define   DEF_MUTLCAP_CC_CAL_SENSE_RESISTOR     RSEL_VAL_100 #define   DEF_MUTLCAP_QUICK_REBURST_ENABLE      1u #define PTC_APBC_BITMASK (1u << 19u) 

Переменные:

static uint8_t mutlcap_data_blk[PRIV_MUTLCAP_DATA_BLK_SIZE];  uint16_t mutlcap_xy_nodes[DEF_MUTLCAP_NUM_CHANNELS * 2] = {DEF_MUTLCAP_NODES};  gain_t mutlcap_gain_per_node[DEF_MUTLCAP_NUM_CHANNELS]= {DEF_MUTLCAP_GAIN_PER_NODE};  freq_hop_sel_t mutlcap_freq_hops[3u]	= {DEF_MUTLCAP_HOP_FREQS}; 

Функция конфигурации тактирования PTC:

void touch_configure_ptc_clock(void) { 	struct system_gclk_chan_config gclk_chan_conf;  	system_gclk_chan_get_config_defaults(&gclk_chan_conf);  	gclk_chan_conf.source_generator = GCLK_GENERATOR_3;  	system_gclk_chan_set_config(PTC_GCLK_ID, &gclk_chan_conf);  	system_gclk_chan_enable(PTC_GCLK_ID);  	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PTC_APBC_BITMASK); } 

Конфигурация сенсоров:

touch_ret_t touch_sensors_config(void) { 	touch_ret_t touch_ret = TOUCH_SUCCESS; 	sensor_id_t sensor_id;  	touch_ret = touch_mutlcap_sensor_config(SENSOR_TYPE_KEY, CHANNEL_0, 			CHANNEL_0, NO_AKS_GROUP, 20u, 			HYST_6_25, RES_8_BIT,0, 			&sensor_id); 	if (touch_ret != TOUCH_SUCCESS) while (1); 		 	  	touch_ret = touch_mutlcap_sensor_config(SENSOR_TYPE_KEY, CHANNEL_1, 			CHANNEL_1, NO_AKS_GROUP, 20u, 			HYST_6_25, RES_8_BIT,0, 			&sensor_id); 	if (touch_ret != TOUCH_SUCCESS) while (1);   	touch_ret = touch_mutlcap_sensor_config(SENSOR_TYPE_ROTOR, CHANNEL_6, 			CHANNEL_9, NO_AKS_GROUP, 20u, 			HYST_6_25, RES_8_BIT,0, 			&sensor_id); 	if (touch_ret != TOUCH_SUCCESS) while (1);  	touch_ret = touch_mutlcap_sensor_config(SENSOR_TYPE_SLIDER, CHANNEL_2, 			CHANNEL_5, NO_AKS_GROUP, 20u, 			HYST_6_25, RES_8_BIT,0, 			&sensor_id); 	if (touch_ret != TOUCH_SUCCESS) while (1);  	return (touch_ret); } 
Инициализация сенсоров и общих параметров

touch_ret_t touch_sensors_init(void) { 	touch_ret_t touch_ret = TOUCH_SUCCESS;  	/* Setup and enable generic clock source for PTC module. */ 	touch_configure_ptc_clock();  	touch_time.measurement_period_ms = DEF_TOUCH_MEASUREMENT_PERIOD_MS;  	/* Initialize touch library for Mutual Cap operation. */ 	touch_ret = touch_mutlcap_sensors_init(&touch_config); 	if (touch_ret != TOUCH_SUCCESS)         { 		while (1u);    /* Check API Error return code. */ 	}  #if DEF_TOUCH_QDEBUG_ENABLE == 1 	QDebug_Init(); #endif  	/* configure the touch library sensors. */ 	touch_ret = touch_sensors_config(); 	if (touch_ret != TOUCH_SUCCESS)          { 		while (1u);    /* Check API Error return code. */ 	}  	/* Auto Tuning setting for calibration. 	 * 	 * AUTO_TUNE_PRSC: When Auto tuning of pre-scaler is selected 	 * the PTC uses the user defined internal series resistor setting 	 * (DEF_MUTLCAP_SENSE_RESISTOR) and the pre-scaler is adjusted 	 * to slow down the PTC operation to ensure full charge transfer. 	 * 	 * AUTO_TUNE_RSEL: When Auto tuning of the series resistor is 	 * selected the PTC runs at user defined pre-scaler setting speed 	 * (DEF_MUTLCAP_CLK_PRESCALE) and the internal series resistor is 	 * tuned automatically to the optimum value to allow for full 	 * charge transfer. 	 * 	 * AUTO_TUNE_NONE: When manual tuning option is selected (AUTO_TUNE_NONE), 	 * the user defined values of PTC pre-scaler and series resistor is used 	 * for PTC operation as given in DEF_MUTLCAP_CLK_PRESCALE and 	 * DEF_MUTLCAP_SENSE_RESISTOR 	 * 	 */ 	touch_ret = touch_mutlcap_sensors_calibrate(AUTO_TUNE_RSEL); 	if (touch_ret != TOUCH_SUCCESS)          { 		while (1u);    /* Check API Error return code. */ 	}  	return (touch_ret); } 

Измерение и обработчик прерывания по окончанию измерения

void touch_mutlcap_measure_complete_callback( void ) { #if DEF_TOUCH_QDEBUG_ENABLE == 1  	/* Send out the Touch debug information data each time when Touch 	 *   measurement process is completed . 	 *   The Touch Signal and Touch Delta values are always sent. 	 *   Touch Status change, Rotor-Slider Position change and Sensor 	 * Reference 	 *   values can be optionally sent using the masks below. 	 */ 	QDebug_SendData( TOUCH_CHANNEL_REF_CHANGE | 			TOUCH_ROTOR_SLIDER_POS_CHANGE | 			TOUCH_STATUS_CHANGE ); 	/* QT600 two-way QDebug communication application Example. */ 	/* Process any commands received from QTouch Studio. */ 	QDebug_ProcessCommands(); #endif  	if (!(p_mutlcap_measure_data->acq_status & TOUCH_BURST_AGAIN))         { 		/* Set the Mutual Cap measurement done flag. */ 		p_mutlcap_measure_data->measurement_done_touch = 1u; 	} }  touch_ret_t touch_sensors_measure(void) { 	touch_ret_t touch_ret = TOUCH_SUCCESS;  	if (touch_time.time_to_measure_touch == 1u)         { 		/* Start a touch sensors measurement process. */  		touch_ret = touch_mutlcap_sensors_measure( 				touch_time.current_time_ms, 				NORMAL_ACQ_MODE, 				touch_mutlcap_measure_complete_callback);  		if ((touch_ret != TOUCH_ACQ_INCOMPLETE) &&	(touch_ret == TOUCH_SUCCESS))                  { 			touch_time.time_to_measure_touch = 0u; 		}                 else if ((touch_ret != TOUCH_SUCCESS) &&(touch_ret != TOUCH_ACQ_INCOMPLETE))                { 			while (1);  			/* Reaching this point can be due to - 			 *     1. The api has retured an error due to a invalid 			 * input parameter. 			 *     2. The api has been called during a invalid Touch 			 * Library state. */ 		} 	}  	return (touch_ret); } 

В main необходимо инициализировать таймер RTC, инициализировать PTC, настроить sleep режим (по необходимости), и разрешить глобальные прерывания:

Инициализация в main

system_interrupt_enable_global();
//Initialize timer. (RTC actually
timer_init();
//Initialize QTouch library and configure touch sensors.
touch_sensors_init();
NVMCTRL->CTRLB.bit.SLEEPPRM = 3;
system_set_sleepmode(SYSTEM_SLEEPMODE_STANDBY);

Пусть в простейшем случае у нас индицируется светодиодами прикосновение к кнопке и позиция слайдера. Ротор трогать не будем.
В while(1) необходимо добавить функцию засыпания (при необходимости), функцию обработки прикосновения и зажигания соответствующих светодиодов для индикации прикосновения:

Код для while(1)

// Goto STANDBY sleep mode, unless woken by timer or PTC interrupt. 		system_sleep(); // Start touch sensor measurement, if touch_time.time_to_measure_touch flag is set by timer.    	      touch_sensors_measure(); 		if ((p_mutlcap_measure_data->measurement_done_touch == 1u))  		{ 			p_mutlcap_measure_data->measurement_done_touch = 0u;  			// Get touch sensor states 			button1_state = GET_MUTLCAP_SENSOR_STATE(0); 			button2_state = GET_MUTLCAP_SENSOR_STATE(1); 			rotor_state = GET_MUTLCAP_SENSOR_STATE(2); 			slider_state = GET_MUTLCAP_SENSOR_STATE(3);  			if (button1_state)  			{ 				if(button_pressed!=1) 				{ 					port_pin_set_output_level(LED_8_PIN, 0); 					button_pressed=1;					 				} 			} 			else  			{ 				port_pin_set_output_level(LED_8_PIN, 1); 				if (button_pressed==1) 				{ 					button_pressed=0; 				} 			}  			if (button2_state) 			{ 				if(button_pressed!=2) 				{ 					port_pin_set_output_level(LED_9_PIN, 0); 					button_pressed=2; 				} 			}  			else 			{ 				port_pin_set_output_level(LED_9_PIN, 1); 				if (button_pressed==2) 				{ 				   button_pressed=0; 				} 			}  			// Clear all slider controlled LEDs 			port_pin_set_output_level(LED_0_PIN, 1); 			port_pin_set_output_level(LED_1_PIN, 1); 			port_pin_set_output_level(LED_2_PIN, 1); 			port_pin_set_output_level(LED_3_PIN, 1); 			port_pin_set_output_level(LED_4_PIN, 1); 			port_pin_set_output_level(LED_5_PIN, 1); 			port_pin_set_output_level(LED_6_PIN, 1); 			port_pin_set_output_level(LED_7_PIN, 1);  			// If slider is activated 			if(slider_state) 			{ 				 // Parse slider position 				slider_position = GET_MUTLCAP_ROTOR_SLIDER_POSITION(1); 				 				slider_position = slider_position >> 5u; 				 			 				switch(slider_position) 				{ 					case 0: 						port_pin_set_output_level(LED_0_PIN, 0); 						break; 					case 1: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						break; 					case 2: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						port_pin_set_output_level(LED_2_PIN, 0); 						break; 					case 3: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						port_pin_set_output_level(LED_2_PIN, 0); 						port_pin_set_output_level(LED_3_PIN, 0); 						break; 					case 4: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						port_pin_set_output_level(LED_2_PIN, 0); 						port_pin_set_output_level(LED_3_PIN, 0); 						port_pin_set_output_level(LED_4_PIN, 0); 						break; 					case 5: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						port_pin_set_output_level(LED_2_PIN, 0); 						port_pin_set_output_level(LED_3_PIN, 0); 						port_pin_set_output_level(LED_4_PIN, 0); 						port_pin_set_output_level(LED_5_PIN, 0); 						break; 					case 6: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						port_pin_set_output_level(LED_2_PIN, 0); 						port_pin_set_output_level(LED_3_PIN, 0); 						port_pin_set_output_level(LED_4_PIN, 0); 						port_pin_set_output_level(LED_5_PIN, 0); 						port_pin_set_output_level(LED_6_PIN, 0); 						break; 					case 7: 						port_pin_set_output_level(LED_0_PIN, 0); 						port_pin_set_output_level(LED_1_PIN, 0); 						port_pin_set_output_level(LED_2_PIN, 0); 						port_pin_set_output_level(LED_3_PIN, 0); 						port_pin_set_output_level(LED_4_PIN, 0); 						port_pin_set_output_level(LED_5_PIN, 0); 						port_pin_set_output_level(LED_6_PIN, 0); 						port_pin_set_output_level(LED_7_PIN, 0); 						break; 					default: 						port_pin_set_output_level(LED_0_PIN, 1); 						port_pin_set_output_level(LED_1_PIN, 1); 						port_pin_set_output_level(LED_2_PIN, 1); 						port_pin_set_output_level(LED_3_PIN, 1); 						port_pin_set_output_level(LED_4_PIN, 1); 						port_pin_set_output_level(LED_5_PIN, 1); 						port_pin_set_output_level(LED_6_PIN, 1); 						port_pin_set_output_level(LED_7_PIN, 1); 						break; 				} 			} 		}//measurement done flag 

Компилируем, заливаем, наслаждаемся.

ссылка на оригинал статьи http://geektimes.ru/post/266890/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *