如何使用 18F4550 创建双向总线?
How can I create a bidirectional bus using a 18F4550?
基本上,我正在尝试使用 PIC18F4550 的单个端口作为输入和输出。我要将一个 7 段显示器连接到端口的 7 个引脚和同一端口的 4 个引脚。这四个输入用于输入二进制数 0 到 15(F),二进制数将在显示器中显示为十进制数。我使用 PIC C 编译器编写代码。
更新: 在第一条评论之后我修改了代码,我认为它基本上是一样的,我只是直接读取端口 B 的输入并使用开关盒为了发送相应的十进制数,我还添加了一个默认状态,因此当我连接时它应该在显示中为 0。这是连接图,我的老师告诉我我可以忽略逻辑门,但我不确定,如果我想添加,我只需要使用另一个端口,如端口 D 来发送一个高电平或低电平取决于端口 B 的工作方式。
更新 2: 它终于起作用了,似乎我可以忽略逻辑门的事实部分正确,代码比我简单,我只是添加了一个三端口 D 高电平和低电平输出到 74Ls244 和 74HC573 对应输入的 EN 和 OE,因此我可以根据端口 B 的状态(输出或输入)启用或禁用。谢谢你们。
#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);
}
}
警告:不是完整的解决方案,而是一些建议...
我们可以简化您的 main
,因为它有大量的重复代码:
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);
}
}
现在,给定 main
的顶部:
SET_TRIS_B(0b00001111);
DSPIN = input_b();
我的问题是:这应该是 内部 顶部的 while
循环吗???
否则,不需要循环,因为DSPIN
和DSPOUT
将保持不变。
更新:
这完全是猜测,但在粗略地查看了数据表之后...
我认为您混淆了 TRIS
端口的值。 second set_tris_b
调用应该有 0b00000000
来设置输出的所有位(并且 not 0b11111111
作为你有它设置输入的所有位)。
我不确定您是否可以使用 单个 8 位端口来完成此操作。输入值需要四位。但是,您需要七位才能输出到 7 段显示器。
这样加起来超过8位。所以,我认为您需要 两个 端口。由于您使用的是端口“b”,我认为有一个端口“a”。
您可能能够像您所做的那样对输入和输出的给定位进行时分多路复用,但我认为这很冒险,而且我对18F 端口的细节可以肯定。
这里的重构代码假设有一个单独的端口“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);
}
}
但是,如果 18F 允许您即时更改三态位并且您可以连接两个 输入位开关位0-3 和位0-6上的7段输出位,您可以简单地将所有*_a
调用更改为*_b
调用。
更新#2:
经过进一步思考[并且,再次,这是猜测],如果 18F 允许 一个 端口来回切换在输入和输出之间重复,上面代码中的“占空比”[可能]不正确。
两个延迟为1000ms,输出占空比[仅]50%,因此7段显示器可能会严重闪烁。
那是因为,在 tristate/input 模式下,7 段显示 而不是 是用正确的段 mask/output 值
驱动的
解决方案是将占空比增加到(例如)99.44% [象牙皂的纯度 :-)]。因此,大多数 时间,端口处于输出模式。我们只是简单地进入tristate/input模式来采样[DIP开关]输入引脚。
这是一个重构版本,它使端口大部分时间都处于输出模式:
// 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);
}
}
基本上,我正在尝试使用 PIC18F4550 的单个端口作为输入和输出。我要将一个 7 段显示器连接到端口的 7 个引脚和同一端口的 4 个引脚。这四个输入用于输入二进制数 0 到 15(F),二进制数将在显示器中显示为十进制数。我使用 PIC C 编译器编写代码。
更新: 在第一条评论之后我修改了代码,我认为它基本上是一样的,我只是直接读取端口 B 的输入并使用开关盒为了发送相应的十进制数,我还添加了一个默认状态,因此当我连接时它应该在显示中为 0。这是连接图,我的老师告诉我我可以忽略逻辑门,但我不确定,如果我想添加,我只需要使用另一个端口,如端口 D 来发送一个高电平或低电平取决于端口 B 的工作方式。
更新 2: 它终于起作用了,似乎我可以忽略逻辑门的事实部分正确,代码比我简单,我只是添加了一个三端口 D 高电平和低电平输出到 74Ls244 和 74HC573 对应输入的 EN 和 OE,因此我可以根据端口 B 的状态(输出或输入)启用或禁用。谢谢你们。
#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);
}
}
警告:不是完整的解决方案,而是一些建议...
我们可以简化您的 main
,因为它有大量的重复代码:
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);
}
}
现在,给定 main
的顶部:
SET_TRIS_B(0b00001111);
DSPIN = input_b();
我的问题是:这应该是 内部 顶部的 while
循环吗???
否则,不需要循环,因为DSPIN
和DSPOUT
将保持不变。
更新:
这完全是猜测,但在粗略地查看了数据表之后...
我认为您混淆了 TRIS
端口的值。 second set_tris_b
调用应该有 0b00000000
来设置输出的所有位(并且 not 0b11111111
作为你有它设置输入的所有位)。
我不确定您是否可以使用 单个 8 位端口来完成此操作。输入值需要四位。但是,您需要七位才能输出到 7 段显示器。
这样加起来超过8位。所以,我认为您需要 两个 端口。由于您使用的是端口“b”,我认为有一个端口“a”。
您可能能够像您所做的那样对输入和输出的给定位进行时分多路复用,但我认为这很冒险,而且我对18F 端口的细节可以肯定。
这里的重构代码假设有一个单独的端口“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);
}
}
但是,如果 18F 允许您即时更改三态位并且您可以连接两个 输入位开关位0-3 和位0-6上的7段输出位,您可以简单地将所有*_a
调用更改为*_b
调用。
更新#2:
经过进一步思考[并且,再次,这是猜测],如果 18F 允许 一个 端口来回切换在输入和输出之间重复,上面代码中的“占空比”[可能]不正确。
两个延迟为1000ms,输出占空比[仅]50%,因此7段显示器可能会严重闪烁。
那是因为,在 tristate/input 模式下,7 段显示 而不是 是用正确的段 mask/output 值
驱动的解决方案是将占空比增加到(例如)99.44% [象牙皂的纯度 :-)]。因此,大多数 时间,端口处于输出模式。我们只是简单地进入tristate/input模式来采样[DIP开关]输入引脚。
这是一个重构版本,它使端口大部分时间都处于输出模式:
// 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);
}
}