|
|
View previous topic :: View next topic |
Author |
Message |
vtrx
Joined: 11 Oct 2017 Posts: 142
|
12F675 software UART |
Posted: Sun May 29, 2022 8:48 pm |
|
|
I'm using code I picked up from the forum, but it's behaving differently from the simulation.
The program is simple.
Receives a character using the software serial and activates or deactivates a pin.
The problem is that if a pin is high, and I send another character to activate another pin, the pin that was activated goes low and I have to activate it again.
I think this happens because the program is changing the pins when it receives some data.
What could be happening?
Code: |
#include <12F629.h>
//#fuses INTRC_IO,NOWDT,NOBROWNOUT,NOPROTECT,NOMCLR
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES CPD //No EE protection
#FUSES PROTECT //Code not protected from reading
#FUSES NOMCLR //Master Clear pin enabled
#FUSES NOPUT //No Power Up Timer
#FUSES NOBROWNOUT //No brownout reset
#use delay(clock=4000000)
#use rs232(baud=9600, rcv=PIN_A3)
//#use fast_io(a)
char command;
void resetPorts(void) ;
//===============================
#INT_RA
void int_ra_isr(void)
{
if(input_state(pin_A3)==0) //it only works with this command.
{
command = getc();
}
}
//===============================
void resetPorts(void)
{
output_low(PIN_A0);
output_low(PIN_A1);
output_low(PIN_A2);
output_low(PIN_A4);
output_low(PIN_A5);
}//-----------
//-----------------------------------------------------------------------------
void main(void)
{
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
// set_tris_a(0b00001000);
resetPorts();
while(true)
{
if(command=='0')
{
output_high(PIN_A0);
}
if(command=='1')
{
output_low(PIN_A0);
}
//............................
if(command=='2')
{
output_high(PIN_A1);
}
if(command=='3')
{
output_low(PIN_A1);
}
//............................
if(command=='4')
{
output_high(PIN_A2);
}
if(command=='5')
{
output_low(PIN_A2);
}
//.............................
if(command=='6')
{
output_high(PIN_A4);
}
if(command=='7')
{
output_low(PIN_A4);
}
//...........................
if(command=='8')
{
output_high(PIN_A5);
}
if(command=='9')
{
output_low(PIN_A5);
}
//..........................
if(command=='r')
{
resetPorts();
}
//........................
}
} |
In the simulation using Proteus this does not happen.
I'm using compiler version 5.070 |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 125 Location: Bombay, India
|
|
Posted: Sun May 29, 2022 11:36 pm |
|
|
I suspect this to be the Read-Modify-Write issue with bit access on I/O ports. A way to solve this would be to keep a shadow byte variable in RAM which you modify and then whenever modified, write out the variable to the port. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Mon May 30, 2022 1:03 am |
|
|
The problem is occurring because you are _continuously_ writing to the
ports. Even when no command is being received. The 'last' command
received remains in memory, so the code is looping writing the bit for
this, even when a new command starts. You can be in the middle of
doing this write, when the interrupt starts reading the new command.
Code: |
#include <12F629.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
//#FUSES CPD //EE protection enabled
//#FUSES PROTECT //Code protected from reading
//Don't use protection when developing code. Doing so, uses extra
//'lives' of the chip's flash memory. Only enable this when the
//code is finished.
#FUSES NOMCLR //Master Clear pin disabled
#FUSES PUT //Power Up Timer
#FUSES BROWNOUT //Brownout reset enabled
//It is always better to have brownout protection enabled.
//Similarly with the power up timer. Allows the power supply to
//be stable before the code starts running.
#use delay(clock=4000000)
#use rs232(baud=9600, rcv=PIN_A3)
char command=0; //start with command empty
#INT_RA
void int_ra_isr(void)
{
if(input_state(pin_A3)==0) //it only works with this command.
//This is because of how 'getc' works with a software serial. It
//returns _before_ the rising edge of the stop bit. So this interrupt
//will be called again when this arrives. Hence you must check that
//a data byte actually is arriving....
{
command = getc();
}
}
//===============================
void resetPorts(void)
{
output_low(PIN_A0);
output_low(PIN_A1);
output_low(PIN_A2);
output_low(PIN_A4);
output_low(PIN_A5);
}//-----------
//You don't need a 'prototype' for this, since it is declared before main.
//-----------------------------------------------------------------------------
void main(void)
{
resetPorts();
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
while(true)
{
if (command!=0)
{
//Only execute the outputs if a new command arrives
if(command=='0')
{
output_high(PIN_A0);
}
if(command=='1')
{
output_low(PIN_A0);
}
//............................
if(command=='2')
{
output_high(PIN_A1);
}
if(command=='3')
{
output_low(PIN_A1);
}
//............................
if(command=='4')
{
output_high(PIN_A2);
}
if(command=='5')
{
output_low(PIN_A2);
}
//.............................
if(command=='6')
{
output_high(PIN_A4);
}
if(command=='7')
{
output_low(PIN_A4);
}
//...........................
if(command=='8')
{
output_high(PIN_A5);
}
if(command=='9')
{
output_low(PIN_A5);
}
//..........................
if(command=='r')
{
resetPorts();
}
//........................
command=0; //clear the command byte, so output stops till
//a new command arrives.
}
}
}
|
Setting 'command' to zero, stops the output's happening, till a new
byte arrives. This way the serial does not occur while a byte is being
changed.
Also do not enable chip protection while doing debugging. If protection
is enabled, the programmer has to perform a full chip erase every time
you make even a tiny change to the program. This uses extra life cycles
in the chip memory.
It is also always safer to have the BROWNOUT and PUT fuses enabled.
The former means the chip will reset rather than potentially getting hung
if the supply does droop, and the latter ensures the supply is stable
before the chip physically starts running the code.
Presumably your LED's do have current limiting resistors to them.
Otherwise the chip will be resetting when you operate each one. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Mon May 30, 2022 8:36 am |
|
|
It didn't work, the pins change.
putc('0') turns on led(A0).
putc('1') turns off.
if i send putc('6)' A4 turns on but if i later send putc('1'),A0 turns off but A4 turns off. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Mon May 30, 2022 8:43 am |
|
|
Change your #USE_RS232, to:
#use rs232(baud=9600, rcv=PIN_A3. SAMPLE_EARLY).
You are probably taking so long to actually arrive at the getc, that
the software UART is misreading the last bit.
Are you sure your supply smoothing is good when the LED's switch?.
Again the question about current limiting resistors on the LED's?.
These are essential.
What you are describing is what will happen if the output pins are
overloaded.
5mA maximum on each pin if it is going to go up to Vih. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Mon May 30, 2022 3:46 pm |
|
|
I am back.
I got what I needed by doing it this way.
Code: | #include <12F629.h>
#fuses INTRC_IO,NOWDT,NOBROWNOUT,NOPROTECT,NOMCLR
//#FUSES NOWDT //No Watch Dog Timer
//#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
//#FUSES CPD //No EE protection
//#FUSES PROTECT //Code not protected from reading
//#FUSES NOMCLR //Master Clear pin enabled
//#FUSES NOPUT //No Power Up Timer
//#FUSES NOBROWNOUT //No brownout reset
#FUSES PUT //Power Up Timer
#FUSES BROWNOUT //Brownout reset enabled
#use delay(clock=4000000)
#use rs232(baud=9600, rcv=PIN_A3)
//#use rs232(baud=9600, rcv=PIN_A3, SAMPLE_EARLY)
//#use fast_io(a)
char command=0; //start with command empty
char portas=255;
void resetPorts(void) ;
//===============================
#INT_RA
void int_ra_isr(void)
{
if(input_state(pin_A3)==0) //it only works with this command.
{
command = getc();
}
}
//===============================
void resetPorts(void)
{
output_low(PIN_A0);
output_low(PIN_A1);
output_low(PIN_A2);
output_low(PIN_A4);
output_low(PIN_A5);
}//-----------
//-----------------------------------------------------------------------------
void main(void)
{
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
// set_tris_a(0b00001000);
resetPorts();
while(true)
{
if (command!=0)
{
//Only execute the outputs if a new command arrives
if(command == '0'){bit_clear(portas,0);}
if(command == '1'){bit_set(portas,0);}
if(command == '2'){bit_clear(portas,1);}
if(command == '3'){bit_set(portas,1);}
if(command == '4'){bit_clear(portas,2);}
if(command == '5'){bit_set(portas,2);}
if(command == '6'){bit_clear(portas,3);}
if(command == '7'){bit_set(portas,3);}
if(command == '8'){bit_clear(portas,4);}
if(command == '9'){bit_set(portas,4);}
if(bit_test(portas,0)==0){output_high(PIN_A0);}
if(bit_test(portas,0)==1){output_low(PIN_A0);}
if(bit_test(portas,1)==0){output_high(PIN_A1);}
if(bit_test(portas,1)==1){output_low(PIN_A1);}
if(bit_test(portas,2)==0){output_high(PIN_A2);}
if(bit_test(portas,2)==1){output_low(PIN_A2);}
if(bit_test(portas,3)==0){output_high(PIN_A5);}
if(bit_test(portas,3)==1){output_low(PIN_A5);}
if(bit_test(portas,4)==0){output_high(PIN_A4);}
if(bit_test(portas,4)==1){output_low(PIN_A4);}
if(command=='r')
{
resetPorts();
}
//........................
command=0; //clear the command byte, so output stops till
//a new command arrives.
}
}
} |
Any tips to improve this code or any ideas why it works this way?
I'm actually using the PIC12F675, would that change anything because the include is for 12F629? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Mon May 30, 2022 10:27 pm |
|
|
I've been avoiding going this way, since it does not get rid of the
fundamental problem that your chip's outputs are overloaded.
Key is that when a pic output is set, the chip internally reads what
is on all the pins of he port, sets the bit required, and writes the
whole byte to the port.
Now _if_ an output is set high, but is not above the Vih of the chip,
when it is read, it is read as low instead of high. This then means that
this pin will be set low instead of high. This is the read modify write
problem (RMW). Now if pins are sensibly loaded, this will only happen if
you set two pins quickly one after the other where the capacitance on
the pins delay the actual change.
Now your system does not do anything quick like this, so the only
reason for this to appear is that the pins are actually overloaded.
The standard PIC output will (on a 5v supply), drive to 4v with any
current below about 5mA. So your circuit is putting more than 5mA
of load on the output pins. Hence the repeated question about the
output resistors being used.
A red LED has a typical Vf of about 1.7v,So a resistor of 470R, will
give just fractionally under 5mA, when the pin is driven to 4v. and
the RMW problem should not appear.
That you are having the problem, says that you are drawing more current
than this. Now drawing more than this on all the output pins, will result
in the PIC running hot, and should be unnecessary. High efficiency LED's
will give bright outputs well below this current.
If you are happy with the PIC being loaded like this, then the correct
way to deal with this is as Jerson says, to use a shadow byte. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Tue May 31, 2022 9:12 am |
|
|
The test hardware is super simple.
pins:
1-VDD.
2-1k resistor connected to led1.
3-1k resistor connected to led2.
4-connected to TX from another pic.
5-open.
6-open.
7-open.
8-VSS.
I tried to use Fast_IO and set pins 2,3,5,6 and 7 for output, but nothing changed. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Tue May 31, 2022 9:33 am |
|
|
With 1K resistors, it should work.
What decoupling do you have on the supply?. What is the supply?. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Tue May 31, 2022 10:02 am |
|
|
The decoupling capacitor has a value of 100n.
The supply is the same used in the 'master' pic, 5v from the USB. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Tue May 31, 2022 10:31 am |
|
|
You should be looking at adding a much larger capacitor. 100nF, is great for
blocking the high frequency spikes at the instant' things switch, but not
provide an 'reservoir' to supply current. The 5v from USB is quite high
impedance and may well be having several hundred mV of ripply as the
LED's switch. It is also quite likely to only give perhaps 4+volts rather
than 5v. What have you got the USB driver in the other PIC programmed to
request as power?. You understand that the driver has to tell the host how
much power it expects to need?. USB on an actual computer is not a simple
supply like a USB power socket. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Tue May 31, 2022 6:03 pm |
|
|
I think I found the problem.
It was my mistake.
I programmed using 12F629 as header, and simulated it as 12f629, but my hardware uses 12F675, which is what I have at the moment.
12F629 doesn't have ADC, 12F675 does.
I had to start by disabling the ADCs and it worked with the first code with the suggested change.
Code: | ...
void main(void)
{
setup_comparator( NC_NC_NC_NC ); // disable comparators
setup_adc_ports(NO_ANALOGS); // disable analog inputs
setup_adc(ADC_OFF); // disable A2D
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
... |
I came to this conclusion because I simulated the same code but with 12F675 and some ports stop responding. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Tue May 31, 2022 11:59 pm |
|
|
I think the phrase 'UURGH' applies...
As you could tell from my replies I was becoming certain you had a
hardware issue of some type. I must admit I'm very surprised you managed
to work with the wrong chip. Both simulators and programmers will
normally not allow the wrong chip type to be used. |
|
|
vtrx
Joined: 11 Oct 2017 Posts: 142
|
|
Posted: Thu Jun 09, 2022 7:16 pm |
|
|
New problem.
I added an effect to test and something strange happens.
It's a flashing effect trying not to change the program flow.
Code: | char u_off;
...
void main(void)
{
...
int32 loop_usb=0;
char u_off = 10; ?
while(true)
{
if (command!=0)
{
//Only execute the outputs if a new command arrives
if(command=='0')
{
output_high(PIN_A0);
}
if(command=='1')
{
u_off = 0;
}
command=0;
}
//............................
if(u_off == 0)//?
{
if(loop_usb == 65535)
{
output_high(PIN_A2);
}
if(loop_usb == 131070)
{
output_low(PIN_A2);
loop_usb = 0;
}
}
}
} |
The effect looping works normally, it depends on the u_off variable that needs to have a value of 0.
if I send '1' by serial u_off it should be 0 but it doesn't seem to change.
Looping does not start.
if the u_off variable is initialized to 0 the looping starts.
Any remarks? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Fri Jun 10, 2022 10:52 am |
|
|
As posted, you don't show anything incrementing loop_usb, so it is
not going to flash.... |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|