diff --git a/lib/ir_nikon.c b/lib/ir_nikon.c
new file mode 100644
index 0000000..d17e446
--- /dev/null
+++ b/lib/ir_nikon.c
@@ -0,0 +1,192 @@
+/* This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+/** library to detect Nikon infrared remote control trigger using 38 kHz infrared demodulator
+ * @file
+ * @author King Kévin
+ * @date 2018
+ * @note peripherals used: timer channel @ref ir_nikon_timer
+ */
+
+/* standard libraries */
+#include // standard integer types
+#include // standard boolean type
+
+/* STM32 (including CM3) libraries */
+#include // Cortex M3 utilities
+#include // interrupt handler
+#include // real-time control clock library
+#include // general purpose input output library
+#include // timer utilities
+
+/* own libraries */
+#include "ir_nikon.h" // own definitions
+#include "global.h" // common methods
+
+/** @defgroup ir_nikon_timer timer peripheral used to measure signal timing for signal decoding
+ * @{
+ */
+#define IR_NIKON_TIMER 4 /**< timer peripheral */
+#define IR_NIKON_CHANNEL 3 /**< channel used as input capture */
+#define IR_NIKON_JITTER 20 /**< signal timing jitter in % tolerated in timing */
+#define IR_NIKON_EXTERNAL_PULLUP true /**< if an external pull-up resistor is already present on the infrared demodulator OUT signal */
+/** @} */
+
+volatile bool ir_nikon_trigger_flag = false;
+
+/** the mark and space durations (in us) corresponding to the Nikon IR sequence (static, measured from a remote clone) */
+const uint16_t ir_nikon_sequence[] = {2000, 28000, 400, 1580, 400, 3580, 400, 63200, 2000, 28000, 400, 1580, 400, 3580, 400}; // actually there is a 2 pulse trailer, but we skip it to have a faster trigger: 70000, 540, 7200, 580
+
+void ir_nikon_setup(void);
+{
+ // setup timer to measure signal timing for bit decoding (use timer channel as input capture)
+ rcc_periph_clock_enable(RCC_TIM_CH(IR_NIKON_TIMER, IR_NIKON_CHANNEL)); // enable clock for GPIO peripheral
+ rcc_periph_clock_enable(RCC_TIM(IR_NIKON_TIMER)); // enable clock for timer peripheral
+ rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternative functions
+#if IR_NIKON_EXTERNAL_PULLUP
+ gpio_set_mode(TIM_CH_PORT(IR_NIKON_TIMER, IR_NIKON_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, TIM_CH_PIN(IR_NIKON_TIMER, IR_NIKON_CHANNEL)); // setup GPIO pin as input
+#else
+ gpio_set(TIM_CH_PORT(IR_NIKON_TIMER, IR_NIKON_CHANNEL), TIM_CH_PIN(IR_NIKON_TIMER, IR_NIKON_CHANNEL)); // idle is high (using pull-up resistor)
+ gpio_set_mode(TIM_CH_PORT(IR_NIKON_TIMER, IR_NIKON_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, TIM_CH_PIN(IR_NIKON_TIMER, IR_NIKON_CHANNEL)); // setup GPIO pin as input
+#endif
+ timer_reset(TIM(IR_NIKON_TIMER)); // reset timer state
+ timer_set_mode(TIM(IR_NIKON_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
+ // codes are repeated every 110 ms, thus we need to measure at least this duration to detect repeats correctly
+ // the 16-bit timer is by far precise enough to measure the smallest 560 us burst
+ timer_set_prescaler(TIM(IR_NIKON_TIMER), (70 * (rcc_ahb_frequency / 1000) / (1 << 16)) - 1); // set the prescaler so this 16 bits timer overflow after 70 ms (to ignore the second sequence)
+ timer_ic_set_input(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_IN_TI(IR_NIKON_CHANNEL)); // configure ICx to use TIn
+ timer_ic_set_filter(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_CK_INT_N_8); // use small filter (noise reduction is more important than timing)
+ timer_ic_set_polarity(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_FALLING); // capture on falling edge (IR bursts are active low on IR demodulators)
+ timer_ic_set_prescaler(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
+
+ timer_clear_flag(TIM(IR_NIKON_TIMER), TIM_SR_UIF); // clear flag
+ timer_update_on_overflow(TIM(IR_NIKON_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
+ timer_enable_irq(TIM(IR_NIKON_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
+
+ timer_clear_flag(TIM(IR_NIKON_TIMER), TIM_SR_CCIF(IR_NIKON_CHANNEL)); // clear input compare flag
+ timer_ic_enable(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL)); // enable capture interrupt only when IR burst
+ timer_enable_irq(TIM(IR_NIKON_TIMER), TIM_DIER_CCIE(IR_NIKON_CHANNEL)); // enable capture interrupt
+
+ nvic_enable_irq(NVIC_TIM_IRQ(IR_NIKON_TIMER)); // catch interrupt in service routine
+ timer_enable_counter(TIM(IR_NIKON_TIMER)); // enable timer
+}
+
+/** interrupt service routine called for timer
+ * @note this could be improve by ignoring short noise burst, but works good enough
+ */
+void TIM_ISR(IR_NIKON_TIMER)(void)
+{
+ static uint8_t burst_count = 0; // the mark or space count
+ static uint32_t burst_start = 0; // time of current mark/space start
+ static uint32_t bits = 0; // the received code bits
+ static struct ir_nec_code_t code; // the last code received (don't trust the user exposed ir_nec_code_received)
+ static bool valid = false; // if the last IR activity is a valid code
+
+ if (timer_get_flag(TIM(IR_NIKON_TIMER), TIM_SR_UIF)) { // overflow update event happened
+ timer_clear_flag(TIM(IR_NIKON_TIMER), TIM_SR_UIF); // clear flag
+ goto reset;
+ } else if (timer_get_flag(TIM(IR_NIKON_TIMER), TIM_SR_CCIF(IR_NIKON_CHANNEL))) { // edge detected on input capture
+ uint32_t time = TIM_CCR(IR_NIKON_TIMER, IR_NIKON_CHANNEL); // save captured bit timing (this also clears the flag)
+ time = (time * (TIM_PSC(TIM(IR_NIKON_TIMER)) + 1)) / (rcc_ahb_frequency / 1000000); // calculate time in us
+ if (time < burst_start) { // this should not happen
+ goto error;
+ }
+ time -= burst_start; // calculate mark/space burst time
+ if (0 == burst_count) { // start of very first IR mark for the AGC burst
+ timer_set_counter(TIM(IR_NIKON_TIMER), 0); // reset timer counter
+ burst_start = 0; // reset code timer
+ time = 0; // ignore first burst
+ } else if (1 == burst_count) { // end of AGC mark
+ if (time > 9000 * (100 - IR_NIKON_JITTER) / 100 && time < 9000 * (100 + IR_NIKON_JITTER) / 100) { // AGC mark
+ } else {
+ goto error;
+ }
+ } else if (2 == burst_count) { // end of AGC space
+ if (time > 4500 * (100 - IR_NIKON_JITTER) / 100 && time < 4500 * (100 + IR_NIKON_JITTER) / 100) { // AGC code space
+ bits = 0; // reset previously received bits
+ valid = false; // invalidate previously received code (since this is not a repeat)
+ } else if (time > 2250 * (100 - IR_NIKON_JITTER) / 100 && time < 2250 * (100 + IR_NIKON_JITTER) / 100) { // AGC repeat space
+ if (valid) {
+ code.repeat = true;
+ ir_nec_code_received.repeat = code.repeat;
+ ir_nec_code_received.address = code.address;
+ ir_nec_code_received.command = code.command;
+ ir_nec_code_received_flag = true;
+ goto reset; // wait for next code
+ } else {
+ goto error;
+ }
+ } else {
+ goto reset; // not the correct header
+ }
+ } else if (burst_count <= (1 + 32) * 2) { // the code bits
+ if (burst_count % 2) { // bit mark end
+ if (time > 560 * (100 - IR_NIKON_JITTER) / 100 && time < 560 * (100 + IR_NIKON_JITTER) / 100) { // bit mark
+ } else {
+ goto error;
+ }
+ } else { // bit space end
+ bits <<= 1;
+ if (time > (2250 - 560) * (100 - IR_NIKON_JITTER) / 100 && time < (2250 - 560) * (100 + IR_NIKON_JITTER) / 100) { // bit 1space
+ bits |= 1; // save bit
+ } else if (time > (1125 - 560) * (100 - IR_NIKON_JITTER) / 100 && time < (1125 - 560) * (100 + IR_NIKON_JITTER) / 100) { // bit 0 space
+ bits |= 0; // save bit
+ } else {
+ goto error;
+ }
+ }
+ if ((1 + 32) * 2 == burst_count) { // the code is complete
+ uint8_t address = (bits >> 24) & 0xff; // get 8 address bits
+ uint8_t naddress = (bits >> 16) & 0xff; // get negated 8 address bits
+ uint8_t command = (bits >> 8) & 0xff; // get 8 command bits
+ uint8_t ncommand = (bits >> 0) & 0xff; // get negate 8 commend bits
+ if (0xff != (address ^ naddress)) { // the address and its negative do not match
+ goto error;
+ }
+ if (0xff != (command ^ ncommand)) { // the command and its negative do not match
+ goto error;
+ }
+ valid = true; // remember we have a valid signal
+ code.repeat = false; // this is not a repeat code
+ code.address = address; // save decoded address
+ code.command = command; // save decoded command
+ ir_nec_code_received.repeat = code.repeat; // transfer code to user
+ ir_nec_code_received.address = code.address; // transfer code to user
+ ir_nec_code_received.command = code.command; // transfer code to user
+ ir_nec_code_received_flag = true;
+ ir_nec_code_received_flag = true; // notify user about the new code
+ goto reset; // wait for next code
+ }
+ } else { // this should not happen
+ goto error;
+ }
+ if (burst_count % 2) {
+ timer_ic_set_polarity(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_FALLING); // wait for end of space
+ } else {
+ timer_ic_set_polarity(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_RISING); // wait for end of mark
+ }
+ burst_count++; // wait for next burst
+ burst_start += time; // save current burst start
+ } else { // no other interrupt should occur
+ while (true); // unhandled exception: wait for the watchdog to bite
+ }
+ return;
+error:
+ valid = false; // invalidate previously received code
+reset:
+ timer_ic_set_polarity(TIM(IR_NIKON_TIMER), TIM_IC(IR_NIKON_CHANNEL), TIM_IC_FALLING); // wait for next IR mark burst
+ ignore = false; // don't ignore pulses anymore
+ burst_count = 0; // reset state
+ burst_start = 0; // reset state
+}
diff --git a/lib/ir_nikon.h b/lib/ir_nikon.h
new file mode 100644
index 0000000..61813b2
--- /dev/null
+++ b/lib/ir_nikon.h
@@ -0,0 +1,27 @@
+/* This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+/** library to detect Nikon infrared remote control trigger using 38 kHz infrared demodulator
+ * @file
+ * @author King Kévin
+ * @date 2018
+ * @note peripherals used: timer channel @ref ir_nikon_timer
+ */
+#pragma once
+
+/** set when the Nikon trigger has been received */
+extern volatile bool ir_nikon_trigger_flag;
+
+/** setup peripherals to receive infrared Nikon trigger */
+void ir_nikon_setup(void);