denny9167
Member level 1
I thought I would share My SIRC decoder for anyone interested. I use it to test my IR remotes for my motorized volume controls.
Code:
/*
* File: sirc887.c
* Author: Denny Brown
*
* Created on March 23, 2022, 2:18 PM
*/
// Sony Remote 40kHz 7 bits command (LSB first) then 5 bits address
// there are also 15 bit and 20 bit versions of Sony protocol
//
// 40kHz 7 bits command (LSB first) then 5 bits address
// 2.4ms mark=start
// 0.6sp, 0.6 mark = 0
// 0.6sp, 1.2 mark = 1
//
// tv address=01
// vcr1 address=02
// vcr2 address=03
// radio/cd address=04
// laser disc address=06
// surround sound address=0c
// tuner address=0d
// amp address=10 (theatre / cassette / tuner)
// cd player address=11
// equaliser address=12
// HDMI control address=17
// dvd address=1a
//PIC16F887 Configuration bits settings
// Device: PIC16F887
// BEGIN CONFIG
#pragma config FOSC = INTRC_NOCLKOUT
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
//END CONFIG
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#define _XTAL_FREQ 8000000
//LCD function defines
#define LCD_FIRST_ROW 0x80
#define LCD_SECOND_ROW 0xC0
#define LCD_THIRD_ROW 0x94
#define LCD_FOURTH_ROW 0xD4
#define LCD_CLEAR 0x01
#define LCD_RETURN_HOME 0x02
#define LCD_ENTRY_MODE_SET 0x04
#define LCD_CURSOR_OFF 0x0C
#define LCD_UNDERLINE_ON 0x0E
#define LCD_BLINK_CURSOR_ON 0x0F
#define LCD_MOVE_CURSOR_LEFT 0x10
#define LCD_MOVE_CURSOR_RIGHT 0x14
#define LCD_TURN_ON 0x0C
#define LCD_TURN_OFF 0x08
#define LCD_SHIFT_LEFT 0x18
#define LCD_SHIFT_RIGHT 0x1E
#ifndef LCD_TYPE
#define LCD_TYPE 2 // 0=5x7, 1=5x10, 2=2 lines
#endif
#define IR_RX PORTBbits.RB0 // IR receiver 40KHz
//LCD module connections
#define LCD_RS PORTDbits.RD2
#define LCD_EN PORTDbits.RD3
#define LCD_D4 PORTDbits.RD4
#define LCD_D5 PORTDbits.RD5
#define LCD_D6 PORTDbits.RD6
#define LCD_D7 PORTDbits.RD7
#define LCD_RS_DIR TRISDbits.TRISD2
#define LCD_EN_DIR TRISDbits.TRISD3
#define LCD_D4_DIR TRISDbits.TRISD4
#define LCD_D5_DIR TRISDbits.TRISD5
#define LCD_D6_DIR TRISDbits.TRISD6
#define LCD_D7_DIR TRISDbits.TRISD7
//End LCD module connections
//LCD firmware
__bit RS;
void LCD_Write_Nibble(uint8_t n)
{
LCD_RS = RS;
LCD_D4 = n & 0x01;
LCD_D5 = (n >> 1) & 0x01;
LCD_D6 = (n >> 2) & 0x01;
LCD_D7 = (n >> 3) & 0x01;
// send enable pulse
LCD_EN = 0;
__delay_us(1);
LCD_EN = 1;
__delay_us(1);
LCD_EN = 0;
__delay_us(100);
}
void LCD_Cmd(uint8_t Command)
{
RS = 0;
LCD_Write_Nibble(Command >> 4);
LCD_Write_Nibble(Command);
if((Command == LCD_CLEAR) || (Command == LCD_RETURN_HOME))
__delay_ms(2);
}
void LCD_Set_Cursor(uint8_t col, uint8_t row)
{
switch(row)
{
case 2:
LCD_Cmd(LCD_SECOND_ROW + col - 1);
break;
case 3:
LCD_Cmd(LCD_THIRD_ROW + col - 1);
break;
case 4:
LCD_Cmd(LCD_FOURTH_ROW + col - 1);
break;
default: // case 1:
LCD_Cmd(LCD_FIRST_ROW + col - 1);
}
}
void LCD_Write_String(const char* LCD_Str)
{
uint8_t i = 0;
RS = 1;
while(LCD_Str[i] != '\0')
{
LCD_Write_Nibble(LCD_Str[i] >> 4);
LCD_Write_Nibble(LCD_Str[i++] );
}
}
void LCD_Begin()
{
RS = 0;
LCD_RS = 0;
LCD_EN = 0;
LCD_D4 = 0;
LCD_D5 = 0;
LCD_D6 = 0;
LCD_D7 = 0;
LCD_RS_DIR = 0;
LCD_EN_DIR = 0;
LCD_D4_DIR = 0;
LCD_D5_DIR = 0;
LCD_D6_DIR = 0;
LCD_D7_DIR = 0;
__delay_ms(40);
LCD_Cmd(3);
__delay_ms(5);
LCD_Cmd(3);
__delay_ms(5);
LCD_Cmd(3);
__delay_ms(5);
LCD_Cmd(LCD_RETURN_HOME);
__delay_ms(5);
LCD_Cmd(0x20 | (LCD_TYPE << 2));
__delay_ms(50);
LCD_Cmd(LCD_TURN_ON);
__delay_ms(50);
LCD_Cmd(LCD_CLEAR);
__delay_ms(50);
LCD_Cmd(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME);
__delay_ms(50);
// function prototypes
void get_mark_time(void); // get Sony IR mark time
// global variables
unsigned char counter = 0;
unsigned char bitcount;
unsigned char ir_address;
unsigned char ir_command;
unsigned int mark_time;
void __interrupt() ISR(void){
if (INTCON.T0IF) {
counter++; // increment counter
INTCON.T0IF = 0; // Clear Timer0 overflow interrupt flag
}
}
/*************************** main function *********************/
void main(void)
{
OSCCON = 0x70; // set internal oscillator to 8MHz
ANSELH = 0; // configure all PORTB pins as digital
TRISD = 0x00;
OPTION_REG = 0x84; // TMR0 prescaler set to 1:32, pull-ups enable
INTCON = 0xC8; // enable global, peripheral and PORTB change interrupts
// start timer 0 counting
INTCON.GIE = 1; // Global interrupt enable
INTCON.T0IE = 1; // Enable Timer0 overflow interrupt
__delay_ms(1000); // wait 1 second
LCD_Begin(); // initialize LCD module
LCD_Set_Cursor(3, 1);
LCD_Write_String("SIRC Protocol");
LCD_Set_Cursor(5, 2);
LCD_Write_String("Decoder");
__delay_ms(4000); //wait 2 seconds
LCD_Cmd(LCD_CLEAR);
LCD_Set_Cursor(3, 1);
LCD_Write_String("Brown Audio");
LCD_Set_Cursor(5, 2); // move cursor to column 1, row 2
LCD_Write_String(" Labs ");
__delay_ms(4000);
LCD_Cmd(LCD_CLEAR);
LCD_Set_Cursor(1, 1); // move cursor to column 1, row 1
LCD_Write_String("Command:0x00");
LCD_Set_Cursor(1, 2); // move cursor to column 1, row 2
LCD_Write_String("Address:0x00");
while(1)
{
ir_command = 0; // initialise to prevent false trigger
ir_address = 0; // initialise to prevent false trigger
get_mark_time(); // get Sony leader - 2.4mS Mark, 1.2mS space
if ((mark_time > 0x80) && (mark_time < 0xB0)){ // ignore anything but 2.4mS mark
for(bitcount = 0 ; bitcount < 7 ; bitcount++){ // 7 bit command
get_mark_time(); // get a Sony IR bit
ir_command >>= 1; // shift
ir_command &= 0x7f; // top bit zero
if (mark_time > 0x40){ // > 40 is assumed to be a 1
ir_command ^= 0x80; // top bit 1
}
}
ir_command >>= 1; // shift 1 unused bit
ir_command &= 0x7F; // clear top bit
for(bitcount = 0 ; bitcount < 5 ; bitcount++){ // 5 bit address
get_mark_time(); // get a Sony IR bit
ir_address >>= 1; // shift
ir_address &= 0x7f; // top bit zero
if (mark_time > 0x40){
ir_address ^= 0x80; // top bit 1
}
}
ir_address >>= 3; // shift 3 unused bits
ir_address &= 0x1F; // clear top 3 bits
}
sprintf(text,"%02X",ir_command);
LCD_Set_Cursor(11, 1); // move cursor to column 11 line 1
LCD_Write_String(text); // print address
sprintf(text,"%02X",ir_address);
LCD_Set_Cursor(11, 2); // move cursor to column 7 line 2
LCD_Write_String(text); // print command
}
}
// get time of mark, then ignore space
void get_mark_time(void){
while(IR_RX); // wait for a mark
counter=0;
TMR0 = 0;
while(!IR_RX); // wait for space
mark_time = (counter << 8) + TMR0; // collect integer mark time
}