Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

IR in C

Status
Not open for further replies.

AtomSoft

Well-Known Member
Ok need some help. This is not working . I think its the way im shifting the bits. Not sure tho. I know the timing is good. But it doesnt work. A better question is how does one send out LSB first? Here is my code.

Code:
#include <p18f1320.h>
#include <stdio.h>
#include <delays.h>

#pragma config WDT = OFF, OSC = INTIO2, LVP = OFF

#define ir		LATBbits.LATB3
#define ir_tris TRISBbits.TRISB3

#define btn1    PORTBbits.RB5
#define btn2    PORTBbits.RB1

#define btn1_tris TRISBbits.TRISB5
#define btn2_tris TRISBbits.TRISB1
//// DELAY TEMPS & CONFIG

char d_tmp;              //delay calculation temp
char lt,lt2;             //loop temp


void main(void);
void ir_byte(char add, char cmd);

void delay_s(char sec);
void delay_ms(char msec);
void delay_us(char usec);

void main(void){
    
    OSCCON = 0x72;   //8MHz

    btn1_tris = 1;
    btn2_tris = 1;
    ir_tris = 0;
    ir = 0;

    while(1){
        if(btn1 == 1){
            //Do something
            ir_byte(0x0B,0x00);  //Actual Command is 0x0D but since this is LSB first its 0x0B
        }

        if(btn2 == 1){
            //Do something
            ir_byte(0x0B,0x01);
        }
    }
}

void ir_byte(char add, char cmd){
    char bl;                //Byte Loop
    char dev;
    char com;
///Preperations:
    com=cmd<<1;
    dev=add<<3;
////////////////////////////////////////
//Send Start////////////////////////////
//turn on for 2.4ms/////////////////////
////////////////////////////////////////
    ir = 1;
    Delay1KTCYx(4);
    Delay100TCYx(8);

    ir = 0;
    Delay1KTCYx(1);
    Delay100TCYx(2);
////////////////////////////////////////
//Send Command//////////////////////////
////////////////////////////////////////

    for(bl=0;bl<7;bl++){
        ir = 1;

        if((com & 0x80) != 0){
            // cmd = 1 delay 1.2ms
            Delay1KTCYx(2);
            Delay100TCYx(4);
        } else {
            // cmd = 0 delay 600us
            Delay1KTCYx(1);
            Delay100TCYx(2);
        }

        //Turn off IR for 600us (space)
        ir = 0;
        Delay1KTCYx(1);
        Delay100TCYx(2);
        com=com<<1;   //Shift data 1 bit over
    }
////////////////////////////////////////
//Send Address//////////////////////////
////////////////////////////////////////
    for(bl=0;bl<5;bl++){
        ir = 1;

        if((dev & 0x80) != 0){
            // add = 1 delay 1.2ms
            Delay1KTCYx(2);
            Delay100TCYx(4);
        } else {
            // add = 0 delay 600us
            Delay1KTCYx(1);
            Delay100TCYx(2);
        }

        //Turn off IR for 600us (space)
        ir = 0;
        Delay1KTCYx(1);
        Delay100TCYx(2);
        dev=dev<<1;   //Shift data 1 bit over
    }

    delay_ms(45);
////////////////////////////////////////
///// DONE /////////////////////////////
////////////////////////////////////////
}

void delay_s(char sec)
{

     for(lt=0;lt<sec;lt++)
          Delay10KTCYx(200);
}
void delay_ms(char msec)
{
     for(lt=0;lt<msec;lt++)
          Delay1KTCYx(2);
}
void delay_us(char usec)
{
    for(lt2=0;lt2<usec;lt2++){
        Delay1TCY();
        Delay1TCY();
    }
}
 
how to i determine the modulation im sending? Also i notice it was 0x16 and not 0x0B. But still now working.

So whats the formula for calculating the khz?
 
how to i determine the modulation im sending? Also i notice it was 0x16 and not 0x0B. But still now working.

So whats the formula for calculating the khz?

Divide one by the frequency, then divide that by two, that gives you each half cycle time. Then send as many cycles as you need to equal the bit time you're sending.
 
wow u confused me quick lol
I used the logic tool with PIckit 2 and:
START = 2.4ms
Logical 1 = 1.2ms
Logical 0 = 600us
Space = 600us

So im not sure why its not working. Here are the pics from the tool:

Full Length:
length-png.22544


Logical 0:
log0-png.22541


Logical 1:
log1-png.22542


Space:
space-png.22543
 

Attachments

  • log0.png
    log0.png
    5.5 KB · Views: 382
  • log1.png
    log1.png
    5.5 KB · Views: 351
  • space.png
    space.png
    5.5 KB · Views: 377
  • length.png
    length.png
    5.5 KB · Views: 347
Last edited:
wow u confused me quick lol
I used the logic tool with PIckit 2 and:
START = 2.4ms
Logical 1 = 1.2ms
Logical 0 = 600us
Space = 600us

So im not sure why its not working. Here are the pics from the tool:


Insted of generating pulses this way, you need to generate 38 or 40KHz and pulse that .. the easiest way is to use PWM at 38/40KHz (freq is different for different receivers) with 50% duty cycle and then pulse it with "start_pwm" and "stop_pwm" (however that function is called in this C you are using) instead of seting port high and low... (so instead of ir=0; you use stop_pwm)

the other way is to use 2 lines (not good way), one is 38KHz and other is your IR and you connect them to the output using and gate

the "best" way, but bit more complex is to send "pulse" at 38KHz .. so your
ir=1;
delay(x);
ir=0;

becomes:
for (i=0;i<x/y/2;i++){
ir=1;
delay(y);
ir=0;
delay(y);
}


y is the length if the halfcycle for the freq you need (nigel already wrote how to calculate it)
clear?
 
Last edited:
You can use the PWM module to produce the 38kHz signal but your output has to be on RB3.

To get 38kHz I think you need to do the following,
Code:
    T2CON=0x04;             //timer 2 on
    CCP1CON=0b00001100;     //enable PWM
    PR2=52;                 //period for 38kHz
    CCPR1L=26;              //50% duty cycle
    TRISBbits.TRISB3=0;     //better make it output

To turn it off set CCPR1L to zero and to turn it on set it to 26.

If it works check the frequency with your pickit2.

Mike.
 
oh i see i totally forgot. Its a series of pulsed that make up a fule pulse aka start, 0,1, space. No wounder lol. I feel se stupid. I can do it in code. I have a data sheet somewhere .

PWM isnt a option here. This will be ported to a non PWM micro(meaning already in use or etc..). Thanks ill give it a go and respond here. Thanks to all.

EDIT: IT WAS AN1064
sony-png.22545
 

Attachments

  • sony.png
    sony.png
    6.5 KB · Views: 403
Last edited:
Wow either my pic is retarded or ? i dont know. Im wrote this code using the code i have in ASM that i know worked when i wrote in ASM tried to port it over.
Code:
#include <p18f1320.h>
#include <stdio.h>
#include <delays.h>

#pragma config WDT = OFF, OSC = INTIO2, LVP = OFF

#define ir		LATBbits.LATB3
#define ir_tris TRISBbits.TRISB3

#define btn1    PORTBbits.RB5
#define btn2    PORTBbits.RB1

#define btn1_tris TRISBbits.TRISB5
#define btn2_tris TRISBbits.TRISB1
//// DELAY TEMPS & CONFIG

char d_tmp;              //delay calculation temp
char lt,lt2;             //loop temp


void main(void);
void ir_byte(char add, char cmd);
void PulseStart(void);
void PulseHigh(void);
void PulseLow(void);
void PulseSpace(void);

void delay_s(char sec);
void delay_ms(char msec);
void delay_us(char usec);


void main(void){
    
    OSCCON = 0x72;
    btn1_tris = 1;
    btn2_tris = 1;
    ir_tris = 0;
    ir = 0;

    while(1){
        if(btn1 == 1){
            //Do something
            ir_byte(0x0D,0x00);  //Actual Command is 0x0D but since this is LSB first its 0x0B
        }

        if(btn2 == 1){
            //Do something
            ir_byte(0x16,0x01);
        }
    }
}

void ir_byte(char add, char cmd){
    char bl;                //Byte Loop

////////////////////////////////////////
//Send Start////////////////////////////
//turn on for 2.4ms/////////////////////
////////////////////////////////////////
    ir = 1;
    PulseStart();     //Start
    ir = 0;
    PulseSpace();
////////////////////////////////////////
//Send Command//////////////////////////
////////////////////////////////////////

    for(bl=0;bl<7;bl++){
        ir = 1;

        if((cmd & 0x01) != 0){
            // cmd = 1 delay 1.2ms
            PulseHigh();
        } else {
            // cmd = 0 delay 600us
            PulseLow();
        }

        //Turn off IR for 600us (space)
        ir = 0;
        PulseSpace();
        cmd=cmd>>1;   //Shift data 1 bit over
    }
////////////////////////////////////////
//Send Address//////////////////////////
////////////////////////////////////////
    for(bl=0;bl<5;bl++){
        ir = 1;

        if((add & 0x01) != 0){
            // add = 1 delay 1.2ms
            PulseHigh();
        } else {
            // add = 0 delay 600us
            PulseLow();
        }

        //Turn off IR for 600us (space)
        ir = 0;
        PulseSpace();
        add=add>>1;   //Shift data 1 bit over
    }

    delay_ms(45);
////////////////////////////////////////
///// DONE /////////////////////////////
////////////////////////////////////////
}

void PulseHigh(void){
    char i;
    for(i=0;i<48;i++){
        ir = 1; 
        delay_us(7);
        ir=0;
        delay_us(18);
    }
}
void PulseLow(void){
    char i;
    for(i=0;i<24;i++){
        ir = 1; 
        delay_us(7);
        ir=0;
        delay_us(18);
    }
}
void PulseSpace(void){
    char i;
    for(i=0;i<24;i++){
        ir = 0; 
        delay_us(25);
   }
}
void PulseStart(void){
    char i;
    for(i=0;i<96;i++){
        ir = 1; 
        delay_us(7);
        ir=0;
        delay_us(18);
    }
}

void delay_s(char sec)
{

     for(lt=0;lt<sec;lt++)
          Delay10KTCYx(200);
}
void delay_ms(char msec)
{
     for(lt=0;lt<msec;lt++)
          Delay1KTCYx(2);
}
void delay_us(char usec)
{
    for(lt2=0;lt2<usec;lt2++){
        Delay1TCY();
        Delay1TCY();
    }
}

Is it possible for INTERNAL OSC ISSUE? Anyway im going to use another chip i have available in case its my old 1320 thats the main issue.

Im also going to try to do a full re-write of the code from the asm code i have.
ASM
Code:
;AtomSoft Conversion to 18F1320
;Tutorial 5.2 - Nigel Goodwin 2002
;Sony SIRC IR transmitter
	LIST	p=18F1320		;tell assembler what chip we are using
	include	<p18F1320.inc>		;include the defaults for the chip
	CONFIG	OSC = INTIO2, WDT = OFF, LVP = OFF, DEBUG = ON		;sets the configuration settings (oscillator type etc.)

	cblock 	0x00 			;start of general purpose registers
		count1 			;used in delay routine
		counta 			;used in delay routine 
		countb
		count
		Delay_Count
		Bit_Cntr
		Data_Byte
		Dev_Byte
		Rcv_Byte
		Pulse
	endc
	

IR_PORT	Equ	PORTB
IR_TRIS	Equ	TRISB
IR_Out	Equ	0x01

SW1	Equ	5			;set constants for the switches
SW2	Equ	3

VolUp Equ 0x12
VolDn Equ 0x13

		org	0x00
Start
		movlw	0x62			; 4MHz clock select
    	movwf	OSCCON
		clrf	IR_PORT			;make PortB outputs low

   		movlw 	b'11111101'		;set PortB all inputs, except RB1
   		movwf 	IR_TRIS
		movlw 	b'11111101'
		movwf	LATB
		movlw 	b'00111000'
		movwf	PORTB

Read_Sw
		call	Delay27
		btfss	PORTB, SW1
		call	Switch1
		btfss	PORTB, SW2
		call	Switch2
		goto	Read_Sw

Switch1	movlw	VolUp
		call	Xmit_RS232
		retlw	0x00

Switch2	movlw	VolDn
		call	Xmit_RS232
		retlw	0x00

;Switch3	movlw	But3
;		call	Xmit_RS232
;		retlw	0x00

TX_Start	movlw	d'92'
		call	IR_pulse
		movlw	d'23'
		call	NO_pulse
		retlw	0x00

TX_One		movlw	d'46'
		call	IR_pulse
		movlw	d'23'
		call	NO_pulse
		retlw	0x00

TX_Zero		movlw	d'23'
		call	IR_pulse
		movlw	d'23'
		call	NO_pulse
		retlw	0x00

IR_pulse			 	
		MOVWF	count		;  Pulses the IR led at 38KHz
irloop	BSF	IR_PORT,	IR_Out 
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		BCF	IR_PORT,	IR_Out
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP
		NOP
		NOP			;
		NOP			;
		DECFSZ	count,F
		GOTO 	irloop	
		RETLW	0

NO_pulse			 	
		MOVWF	count		;  Doesn't pulse the IR led
irloop2		BCF	IR_PORT,	IR_Out 
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			;
		NOP			; 
		NOP			;
		NOP			;
		NOP			;
		BCF	IR_PORT,	IR_Out
		NOP			;
		NOP			;	
		NOP			;
		NOP			;	
		NOP			;
		NOP			;
		NOP
		NOP
		NOP			;
		NOP			;
		DECFSZ	count,F
		GOTO 	irloop2	
		RETLW	0

Xmit_RS232      MOVWF   Data_Byte            	;move W to Data_Byte
                MOVLW   0x07                 	;set 7 DATA bits out
                MOVWF   Bit_Cntr
                call	TX_Start		;send start bit
Ser_Loop        RRCF     Data_Byte , f        	;send one bit
                BTFSC   STATUS    , C
                call	TX_One
                BTFSS   STATUS    , C
                call	TX_Zero
                DECFSZ  Bit_Cntr  , f        	;test if all done
                GOTO    Ser_Loop

				movlw	0x10			; VOL CONTROL DEVICE ID

				movwf	Dev_Byte		
				MOVLW   0x05                 	;set 5 device bits out
                MOVWF   Bit_Cntr
Ser_Loop2       RRCF     Dev_Byte , f        	;send one bit
                BTFSC   STATUS    , C
                call	TX_One
                BTFSS   STATUS    , C
                call	TX_Zero
                DECFSZ  Bit_Cntr  , f        	;test if all done
                GOTO    Ser_Loop2
                retlw	0x00



;Delay routines

Delay255	movlw	0xff		;delay 255 mS
		goto	d0
Delay100	movlw	d'100'		;delay 100mS
		goto	d0
Delay50		movlw	d'50'		;delay 50mS
		goto	d0
Delay27		movlw	d'27'		;delay 27mS
		goto	d0
Delay20		movlw	d'20'		;delay 20mS
		goto	d0
Delay5		movlw	0x05		;delay 5.000 ms (4 MHz clock)
d0		movwf	count1
d1		movlw	0xC7
		movwf	counta
		movlw	0x01
		movwf	countb
Delay_0		decfsz	counta, f
		goto	$+2
		decfsz	countb, f
		goto	Delay_0

		decfsz	count1	,f
		goto	d1
		retlw	0x00

;end of Delay routines

	end
 
Last edited:
oh i see i totally forgot. Its a series of pulsed that make up a fule pulse aka start, 0,1, space. No wounder lol. I feel se stupid. I can do it in code. I have a data sheet somewhere .

PWM isnt a option here. This will be ported to a non PWM micro(meaning already in use or etc..). Thanks ill give it a go and respond here. Thanks to all.

I've always considered it better to do it in software anyway, it gives simpler and more accurate control over the pulses - and it's not as if the processor is doing anything else.

Considering this is Sony SIRC's, my tutorials give assembler examples for the 16F - so you can just copy the number of cycles in the loops.
 
Im about to quit this whole project. I dont want to sound negative but this makes no sense.

What delays generate bit timing? And what exactly needs to happen. I know how many uS needs be where to generate a good pulse.

Total was 25uS each and the high point is 7uS right. So why wont a simple delay work here?

Like pulse the pin high for 7uS and low for 18uS. For a how long? 48 times was "1" & 24 was a "0". And a Solid Low for 25us should be a space. Am i right? So why wont that work if thats how its supposed to be. Or is that only for 4Mhz if so i need way more info on what to change. I know it seems like im not trying but my brain is about to explode.

Nigel i know your a busy man but also a smart one so if you have some if any time to explain this to me a little more i would be thankful to the fullest.
 
also how would doubling the cycle count double the frequency? If it was made for 4mhz that 1 uS per cycle and at 8mhz its 2 uS per cycle. So if i double it shouldnt it be the same as if it was 4 mhz since 8 mhz cuts it in half?
 
Total was 25uS each and the high point is 7uS right. So why wont a simple delay work here?

Like pulse the pin high for 7uS and low for 18uS. For a how long? 48 times was "1" & 24 was a "0". And a Solid Low for 25us should be a space. Am i right? So why wont that work if thats how its supposed to be. Or is that only for 4Mhz if so i need way more info on what to change. I know it seems like im not trying but my brain is about to explode.

You have it correct. However, doing short delays in C is tricky as you don't know how long a call or a for next loop will take.

Anyway, this looked like a fun project and so I had a play with it today, hope you don't mind.

First, I worked out a call take 13 cycles. I then wrote a short delay routine,
Code:
void Delay13p3W(unsigned char W){
    _asm
DL: addlw   0xff
    bnz    DL
    _endasm
}
This will delay 13+(W*3) cycles.

I then wrote a for next loop that would send a zero bit, a zero bit consists of 24 pulses of the 40kHz signal,
Code:
    for(i=0;i<24;i++){      //12 cycles
        PORTBbits.RB3=1;    //1
        Delay13p3W(1);      //1*3+13=16
        PORTBbits.RB3=0;    //1
        Delay13p3W(2);      //3*3+13=22      
    }
This has an on time of 7.5uS and a total time of 26uS. Close enough.
Note the cycles are double the uS.

Finally, I put the whole lot together,
Code:
void OutIR(unsigned char cmd, unsigned char dev){
unsigned char i;
    Pulse(2);           //send start bit
    for(i=0;i<7;i++){   //and 7 command bits
        Pulse(cmd&1);   //send lsb first
        cmd>>=1;        //move next bit into lsb
    }
    for(i=0;i<5;i++){   //now send 5 device bits
        Pulse(dev&1);
        dev>>=1;
    }
}

void Pulse(unsigned char Bit){
unsigned char i;
    if(Bit==0){                 //send 24 blips for a zero
        for(i=0;i<24;i++){      //12 cycles
            PORTBbits.RB3=1;    //1
            Delay13p3W(1);      //1*3+13=16
            PORTBbits.RB3=0;    //1
            Delay13p3W(2);      //3*3+13=22      
        }
    }else if(Bit==1){           //48 for a 1
        for(i=0;i<48;i++){
            PORTBbits.RB3=1;
            Delay13p3W(1);
            PORTBbits.RB3=0;
            Delay13p3W(3);
        }
    }else{                      //must be a start pulse
        for(i=0;i<96;i++){      //so do 96 blips
            PORTBbits.RB3=1;
            Delay13p3W(1);
            PORTBbits.RB3=0;
            Delay13p3W(3);
        }
    }
    Delay13p3W(196);            //Do 600uS pause. 196*3+13 = 601
    Delay13p3W(196);            //do it again as we are at 8MHz
}

I haven't tried it but I think the above should work. It looks right on my scope.

Mike.
 
the math is pretty simple ...

8Mhz with 4 ticks per instruction => 2000000 instructions per second => 1/2000000 sec per instruction (0.0000005 sec)

you need to generate 38KHz square wave signal 1010101010101010....

10 is the "period" that takes 1/38000 sec (0.000026316 sec) hence
1 last 0.000026316/2 and 0 last 0.000026316/2 (0.000013158)

with half period lasting 0.000013158 and one nop lasting 0.0000005 you need
0.000013158 / 0.0000005 = 26.316 instructions

so, one 38KHz pulse is

ir=1; // 1
nop ; //2
nop ; //3
..
nop; //25
nop; //26

ir=0; // 1
nop ; //2
nop ; //3
..
nop; //25
nop; //26

so .. this ping last 0.000026316 sec ... now, you want to send your data ... as you wrote
Logical 1 = 1.2ms
Logical 0 = 600us
Space = 600us

so .. to send one, you need to be sending these pulses for 1.2ms (0.0012 sec) .. if we remember that 38KHz ping last 0.000026316 sec we need to send
0.0012 / 0.000026316 = 45.599 pings to send 1 (you can round to 45 or 46 - it works)

to send 0, same thing only 0.0006 / 0.000026316 = 22.8 (round to 23)

that's about it, I hope it is clear .. now, what you also have to worry about is if you put nop's in the loop you have to account for branch that takes 2x time as normal instruction, if you put it in function you have to account for the function call etc etc .. the easier way is to measure those timings with pickit2 or oscilloscope and then remove excess nop's, or if you plan on using some particular c compiler in future, inspect the asm code it generates for loops/function calls and you will know how many instructions it takes to do certain things

EDIT: Mike was faster :) and he wrote the code for you (only 40KHz .. here is the calc with 38 but I hope you now understand the math)
 
Last edited:
also how would doubling the cycle count double the frequency? If it was made for 4mhz that 1 uS per cycle and at 8mhz its 2 uS per cycle. So if i double it shouldnt it be the same as if it was 4 mhz since 8 mhz cuts it in half?

You're wanting to generate a specific number of cycles, in a specific time period (either 0.6, 1.2, or 2.4mS) - if you double the number of cycles in those periods, you obviously double the frequency.

Like I said, you need to adjust the half cycle timing so as to give the SAME number of cycles in the same period (keeping the frequency the same).
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top