Basically what Im trying to do its to use a single port of the PIC18F4550 as both inputs and outputs. Im going to connect a 7-segment-display to 7 pins of the port and with 4 pins of the same port. The four inputs are for introduce the numbers from 0 to 15 (F) in binary and the number in binary will represent in decimal in the display. Im using PIC C Compiler for the code.
Update: Following the first comment I modify the code and I think it is basically the same, I just read directly the input of the port B and use a switch-case in order to send the corresponding decimal number, also I added a default state so when I connected it should be a 0 in the display. Here it is the connection diagram, my teacher told me that I can ignore the logic gates but I'm not shure about that, in case I want to added I just need to use another port like port D to send a single high or low depending on how the port B is working.
Update 2: It finally work, it seems that the fact that I can ignore the logic gates it was partially true, the code was easier than I though, I just added a three port D high and low outputs to the EN and OE of both 74Ls244 and 74HC573 corresponding inputs so I can enable or disable depending on the port B state (output or input). Thank you all.
#include<18F4550.h>
#fuses XT, NOWDT, NOLVP, NOPROTECT, PUT
#use delay(clock = 4M, crystal = 8M)
#use fast_io(b)
void main(){
int8 INDSP;
int8 OUTDSP;
while(true){
SET_TRIS_B(0xFF);
INDSP = input_b()&0x0F;
switch(INDSP){
case 0b00000000:
OUTDSP = 0x3F;
break;
case 0b00000001:
OUTDSP = 0x0C;
break;
case 0b00000010:
OUTDSP = 0x76;
break;
case 0b00000011:
OUTDSP = 0x5E;
break;
case 0b00000100:
OUTDSP = 0x4D;
break;
case 0b00000101:
OUTDSP = 0x5B;
break;
case 0b00000110:
OUTDSP = 0x7B;
break;
case 0b00000111:
OUTDSP = 0x0E;
break;
case 0b00001000:
OUTDSP = 0x7F;
break;
case 0b00001001:
OUTDSP = 0x4F;
break;
case 0b00001010:
OUTDSP = 0x6F;
break;
case 0b00001011:
OUTDSP = 0x79;
break;
case 0b00001100:
OUTDSP = 0x33;
break;
case 0b00001101:
OUTDSP = 0x7C;
break;
case 0b00001110:
OUTDSP = 0x73;
break;
case 0b00001111:
OUTDSP = 0x63;
break;
DEFAULT:
OUTDSP = 0x3F;
break;
}
delay_ms(500);
SET_TRIS_B(0x00);
output_b(OUTDSP);
delay_ms(500);
}
}
Caveat: Not a total solution, but some suggestions ...
We can simplify your main
as it has large amounts of replicated code:
void
main()
{
SET_TRIS_B(0b00001111);
DSPIN = input_b();
while (true) {
if ((DSPIN >= 0b00000000) && (DSPIN <= 0b00001111))
DSPOUT = DSPIN;
else
DSPOUT = 0;
delay_ms(1000);
SET_TRIS_B(0b11111111);
if ((DSPOUT >= 0) && (DSPOUT <= 15))
output_b(DSPVALOR[DSPOUT]);
else
output_b(DSPVALOR[0]);
delay_ms(1000);
}
}
Now, given the top part of main
:
SET_TRIS_B(0b00001111);
DSPIN = input_b();
The question I have is: should this be inside the while
loop at the top???
Otherwise, there's no need for a loop as DSPIN
and DSPOUT
will remain constant.
UPDATE:
This is a total guess, but after cursorily looking at the datasheet ...
I think you're mixing up the value for the TRIS
port. The second set_tris_b
call should have 0b00000000
to set all bits for output (and not 0b11111111
as you have which sets all bits for input).
I'm not sure you can do this with a single 8 bit port. You need four bits for the input value. But, you need seven bits for output to the 7 segment display.
This adds up to more than 8 bits. So, I think you need two ports. Since you're using port "b", I presume there is a port "a".
You may be able to time division multiplex a given bit for input and output as you've done, but I think it's dicey and I don't know enough about the specifics of the 18F's ports to say for sure.
Here's refactored code that assumes a separate port "a":
void
main()
{
while (true) {
// set port B lower four bits for input
SET_TRIS_B(0b00001111);
// get input and mask off don't care bits
DSPIN = input_b();
DSPIN &= 0b00001111;
if ((DSPIN >= 0b00000000) && (DSPIN <= 0b00001111))
DSPOUT = DSPIN;
else
DSPOUT = 0;
delay_ms(1000);
// set port A pins for output
SET_TRIS_A(0b00000000);
if ((DSPOUT >= 0) && (DSPOUT <= 15))
output_a(DSPVALOR[DSPOUT]);
else
output_a(DSPVALOR[0]);
delay_ms(1000);
}
}
But, if the 18F lets you change the tristate bits on the fly and you can connect both the input bit switches on bits 0-3 and the 7 segment output bits on bits 0-6, you can simply change all *_a
calls into *_b
calls.
UPDATE #2:
Upon further reflection [and, again, this is speculation], if the 18F allows one port to be switched back and forth repeatedly between input and output, the "duty cycle" in the above code is [probably] incorrect.
With 1000ms for both delays, the duty cycle is [only] 50% for output, so the 7 segment display will probably flicker badly.
That's because, when in tristate/input mode, the 7 segment display is not being driven with the correct segment mask/output value
The solution is to increase the duty cycle to (e.g.) 99.44% [the purity of Ivory Soap :-)]. So, most of the time, the port is in output mode. We only enter tristate/input mode briefly to sample the [DIP switch] input pins.
Here's a refactored version that keeps the port in output mode a high percentage of the time:
// NOTE: these may be need to be adjusted
enum {
INPUT_STABILIZE_DELAY = 1, // as short as possible
OUTPUT_STABILIZE_DELAY = 1, // as short as possible
OUTPUT_HOLD_DELAY = 100, // as long as possible
};
void
main()
{
while (true) {
// set port B lower four bits for input
SET_TRIS_B(0b00001111);
// allow port to stabilize
delay_ms(INPUT_STABILIZE_DELAY);
// get input and mask off don't care bits
DSPIN = input_b();
DSPIN &= 0b00001111;
// set output mode based on input
if ((DSPIN >= 0b00000000) && (DSPIN <= 0b00001111))
DSPOUT = DSPIN;
else
DSPOUT = 0;
// set port B pins for output
SET_TRIS_B(0b00000000);
// allow tristate pins to stabilize
delay_ms(OUTPUT_STABILIZE_DELAY);
// output to 7 segment delay
output_b(DSPVALOR[DSPOUT]);
// ensure that port is in output mode 99.44% of the time
delay_ms(OUTPUT_HOLD_DELAY);
}
}