
In the previous guide (here), we saw how to configure the required peripherals. In this guide we shall see how to develop the algorithm required to decode the data.
In this guide, we shall cover the following:
- Decoding algorithm.
- Code.
- Demo
8. Decoding Algorithm:

In general, the protocol has the following:
- Leader code
- First address
- Second address
- First command
- Second command
In Leader code, there is AGC pulse which is 9.4 milliseconds, hence we can detect this as following once the external interrupt is generated:
We start of by declare a variable to store the pulse length and read the current timer value (which should be zero) as following:
uint16_t len = 0; len = read_nec_timer();
Then switch the decode state:
- Start of AGC pulse
- End of AGC pulse
switch (decoding_state)
{
case AGC_START:
start_nec_timer(); // Start 9 ms pulse measurement
decoding_state = AGC_END; // Begin decoding
return;
case AGC_END:
reset_nec_timer(); // Reset timer
if ((len > MIN_9_MS) && (len < MAX_9_MS)) // ... 9 ms AGC
{
decoding_state = SHORT_PAUSE; // then begin a 4,5 ms..
return; // ..pulse measurement
}
break;Then move the to long pause which is about 4.5 milliseconds
case SHORT_PAUSE:
reset_nec_timer(); // Reset timer
if ((len > MIN_4_5_MS) && (len < MAX_4_5_MS))
{
decoding_state = NEC_ADDR_COMM; // Begin system adress decode
return;
}
break;Then first address:
case NEC_ADDR_COMM:
if ((len > MIN_560_US) && (len < MAX_560_US) &&
(bitn == MAX_BITS) &&
((system & inv_system) == 0) &&
((command & inv_command) == 0))
{
decoding_state = LONG_PAUSE_1; // Begin a long pause measurement
reset_nec_timer(); // Reset timer
bitn = DECODE_IS_OK;
return;
}
else
{
decoding_state = NEC_BITS_DEC;
return;
}
break;Decoding the actual data:
If the pulse length is about 2.25 milliseconds, then data is 1.
If the pulse is about 1.25, then the data is 0.
case NEC_BITS_DEC: // Bits receiving
reset_nec_timer(); // Reset timer
decoding_state = NEC_ADDR_COMM;
bitn++;
if ((len > MIN_2_25_MS) && (len < MAX_2_25_MS))
{
one_is_received(); // Received bit is "1"
return;
}
else if ((len > MIN_1_125_MS) && (len < MAX_1_125_MS))
{
zero_is_received(); // Received bit is "0"
return;
}
break;For adding zero and 1:
void one_is_received(void)
{
if (bitn < START_OF_COM)
{
if (bitn < START_OF_INV_SYS) // Receive system address bits
{
system = ((system >> 1) | 0x80);
}
else
{
inv_system = ((inv_system >> 1) | 0x80);
}
}
else
{
if (bitn < START_OF_INV_COM) // Receive command bits
{
command = ((command >> 1) | 0x80);
}
else
{
inv_command = ((inv_command >> 1) | 0x80);
}
}
}
/* This functon stores the received "zero" bits
*
*/
void zero_is_received(void)
{
if (bitn < START_OF_COM)
{
if (bitn < START_OF_INV_SYS) // Receive system address bits
{
system = ((system >> 1) & 0x7F);
}
else
{
inv_system = ((inv_system >> 1) & 0x7F);
}
}
else
{
if (bitn < START_OF_INV_COM) // Receive command bits
{
command = ((command >> 1) & 0x7F);
}
else
{
inv_command = ((inv_command >> 1) & 0x7F);
}
}
}
The rest of the algorithm (mostly identical to the first):
case NEC_ADDR_COMM:
if ((len > MIN_560_US) && (len < MAX_560_US) &&
(bitn == MAX_BITS) &&
((system & inv_system) == 0) &&
((command & inv_command) == 0))
{
decoding_state = LONG_PAUSE_1; // Begin a long pause measurement
reset_nec_timer(); // Reset timer
bitn = DECODE_IS_OK;
return;
}
else
{
decoding_state = NEC_BITS_DEC;
return;
}
break;
case NEC_BITS_DEC: // Bits receiving
reset_nec_timer(); // Reset timer
decoding_state = NEC_ADDR_COMM;
bitn++;
if ((len > MIN_2_25_MS) && (len < MAX_2_25_MS))
{
one_is_received(); // Received bit is "1"
return;
}
else if ((len > MIN_1_125_MS) && (len < MAX_1_125_MS))
{
zero_is_received(); // Received bit is "0"
return;
}
break;
case LONG_PAUSE_1:
reset_nec_timer(); // Reset timer
if ((len > MIN_40_MS) && (len < MAX_40_MS)) // P
{ // A
decoding_state = REPEAT_AGC; // U
return; // S
} // E
break;
case REPEAT_AGC:
reset_nec_timer(); // Reset timer
if ((len > MIN_9_MS) && (len < MAX_9_MS))
{ // R
decoding_state = REPEAT_SPACE; // E
return; // P
} // E
break; // A
// T
case REPEAT_SPACE:
reset_nec_timer(); // Reset timer
if ((len > MIN_2_25_MS) && (len < MAX_2_25_MS))
{ // C
decoding_state = REPEAT_BURST; // O
return; // D
} // E
break;
case REPEAT_BURST:
reset_nec_timer(); // Reset timer
if ((len > MIN_560_US) && (len < MAX_560_US))
{ // R
decoding_state = LONG_PAUSE_2; // E
rep_code++; // P
return; // E
} // A
break; // T
// .
case LONG_PAUSE_2: // .
reset_nec_timer(); // Reset timer
if ((len > MIN_98_MS) && (len < MAX_98_MS))
{ // C
decoding_state = REPEAT_AGC; // O
return; // D
} // E
break;
default:
reset_nec_timer(); // Reset timer
decoding_state = FAULT; // Indicate an error
dec_ready = 0;
break;
}
For NEC timer start and reset:
void reset_nec_timer(void)
{
TIM2->CNT=0;
TIM2->EGR|=TIM_EGR_UG;
}
uint16_t read_nec_timer(void)
{
return TIM2->CNT; // Get the pulse len
}
void start_nec_timer(void)
{
TIM2->CR1|=TIM_CR1_CEN;
TIM2->CNT=0;
TIM2->EGR|=TIM_EGR_UG;
}
void stop_nec_timer(void)
{
TIM2->CR1&=~TIM_CR1_CEN;
}For reading the data from the main
uint8_t decode_state()
{
if((bitn==DECODE_IS_OK)){return decode_success;}
else {return decode_failed;}
return 0;
}
uint8_t get_system_state()
{
return system;
}
uint8_t read_command()
{
return decoded_data;
}Then inside the header file, we shall add the following:
#define decode_success 1
#define decode_failed 0
uint8_t decode_state();
uint8_t read_command();
uint8_t get_system_state();
enum decoding_state {
AGC_START,
AGC_END,
SHORT_PAUSE,
NEC_ADDR_COMM,
NEC_BITS_DEC,
LONG_PAUSE_1,
REPEAT_AGC,
REPEAT_SPACE,
REPEAT_BURST,
LONG_PAUSE_2,
FAULT
};
#define SYSTEM 0
#define FAULT 0xFF
#define MAX_BITS 32
#define START_OF_INV_SYS 9
#define START_OF_COM 17
#define START_OF_INV_COM 25
#define DECODE_IS_OK 0x55In the main function:
extern void SysClockConfig(void);
#include "stm32f4xx.h"
#include "delay.h"
#include "math.h"
#include "stdlib.h"
#include "NEC.h"
#include "stdio.h"
#include "oled.h"
#include "i2c.h"
int main(void)
{
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /*Enable floating point unit*/
SysClockConfig();
systick_init_ms(64000000);
NEC_Decoder_Init();
SSD1306_Init();
SSD1306_GotoXY(0,0);
SSD1306_Puts("hello world",&Font_11x18,1);
SSD1306_UpdateScreen();
delay(2000);
SSD1306_Clear();
while(1)
{
if(decode_state()==decode_success)
{
if(get_system_state()){
uint8_t data=read_command();
char data_str[20];
sprintf(data_str,"Code=0x%x",data);
SSD1306_Clear();
SSD1306_GotoXY(0,0);
SSD1306_Puts(data_str,&Font_11x18,1);
SSD1306_UpdateScreen();}
}
}
}
9. Code:
You may download the entire source code from here:
10. Demo:
Happy coding 🙂
Add Comment