问题描述
有一个 Code 新手。我想创建标志性的 TV Knight Rider 扫描。我已经写了我的第一个代码并实现了基本功能。我知道有很多(已经)为此编写了代码,并且我查看了各种示例以了解我想要做什么。我在开始时添加了一个全开序列,并在代码中找出了 GOTO。对你们大多数人来说很简单,但对我来说是一个构建块:) 这是我使用 MPLAB X IDE 5.45 和 XC8 的 16F88 的工作代码
#include <stdio.h>
#include <stdlib.h>
#include "16F88_xc8_header.h" // 16F88 CONfig BITS
#define _XTAL_FREQ 4000000 // 4 MHz clock
int main(int argc,char** argv)
{
__CONfig(FOSC_INTRcclK&WDTE_OFF&MCLRE_OFF);
OSCCONbits.IRCF = 0b110; // Binary value of the three ICRF bits to select 4 MHz internal oscillator
TRISB = 0x00;
PORTB = 0x00;
{
PORTB = 0b00111111; // ALL-ON EFFECT
__delay_ms(400);
PORTB = 0b00011111;
__delay_ms(80);
PORTB = 0b00001111;
__delay_ms(80);
PORTB = 0b00000111;
__delay_ms(80);
PORTB = 0b00000011;
__delay_ms(80);
}
{
kitt_scan:
PORTB = 0b00000001; // 6LED SCAN STARTS
__delay_ms(400);
PORTB = 0b00000010;
__delay_ms(400);
PORTB = 0b00000100;
__delay_ms(400);
PORTB = 0b00001000;
__delay_ms(400);
PORTB = 0b00010000;
__delay_ms(400);
PORTB = 0b00100000;
__delay_ms(400);
PORTB = 0b00010000;
__delay_ms(400);
PORTB = 0b00001000;
__delay_ms(400);
PORTB = 0b00000100;
__delay_ms(400);
PORTB = 0b00000010;
__delay_ms(400);
goto kitt_scan;
return (EXIT_SUCCESS);
}
}
接下来我要添加的是 PWM,以便在光图案左右移动时为 LED 提供拖尾效果。我当然用谷歌搜索过,但我发现的是如何让一个 LED 从低到高淡出。这不是我想要的,相反,我希望有一个可以在代码中分配的预定输出值的选项。
例如
0=LED OFF
1=LED at 30% Duty Cycle (very DIM LED)
2=LED at 70% Duty Cycle (somewhat DIM LED)
3=LED at 100% Duty Cycle (full bright LED)
我希望这样编码:
PORTB = 0b00000003;
__delay_ms(400);
PORTB = 0b00000032;
__delay_ms(400);
PORTB = 0b00000321;
__delay_ms(400);
所以我的问题是,我从哪里开始甚至完成这项工作?欢迎提供任何见解。
谢谢 托尼
解决方法
电子产品
我只是将一个电容器与每个 LED 并联,这样当 LED 开启时,它也会为电容器充电,而当 LED 关闭时,LED 会在电容器放电时逐渐消失。 :-)
对于普通 C(或 C++)
警告:我没有测试过这些,所以我的示例代码中可能存在错误。学习修复错误和学习创建错误一样重要,所以如果有错误,你会学到更多!
假设您有一个从零开始并每 1 毫秒递增的计数器;当它达到 4 时,您将计数器重置为零;像这样:
int counter = 0;
nextTick:
__delay_ms(1);
counter++;
if(counter >= 4) {
counter = 0;
}
goto nextTick;
如果您在计数器为零时打开 LED 并在计数器为 1 时关闭 LED,那么 LED 将亮起 25% 的时间并熄灭 75% 的时间。这可能看起来像:
int counter = 0;
PORTB = 0b00000001;
nextTick:
__delay_ms(1);
counter++;
if(counter >= 4) {
counter = 0;
PORTB = 0b00000001;
}
if(counter == 1) {
PORTB = 0b00000000;
}
goto nextTick;
如果您在计数器为 2 时关闭 LED,则 LED 将亮起 50% 的时间;如果您在计数器为 3 时关闭 LED,则 LED 将亮起 75% 的时间;如果您在计数器大于或等于 4 时关闭 LED(这永远不会发生,因为它首先重置为零)LED 将在 100% 的时间内亮起;如果您在计数器为零时关闭 LED,那么 LED 将在“几乎 0%”的时间内亮起。
您可以使用变量来控制“LED 应该亮多少”,例如:
int counter = 0;
int LED0_timeOn = 2;
PORTB = 0b00000001;
nextTick:
__delay_ms(1);
counter++;
if(counter >= 4) {
counter = 0;
PORTB = 0b00000001;
}
if(counter == LED0_timeOn) {
PORTB = 0b00000000;
}
goto nextTick;
这有点笨拙,因为当 LED 应该在 0% 的时间内打开时,您会先打开它,然后再将其关闭。您可以通过使用变量并且每个刻度只设置一次 PORTB 来避免这种情况,如下所示:
int counter = 0;
int nextPortBvalue = 0b00000001;
int LED0_timeOn = 2;
nextTick:
PORTB = nextPortBvalue;
__delay_ms(1);
counter++;
if(counter >= 4) {
counter = 0;
nextPortBvalue = 0b00000001;
}
if(counter == LED0_timeOn) {
nextPortBvalue = 0b00000000;
}
goto nextTick;
如果你有 6 个变量,你可以有 6 个 LED,每个 LED 都有不同的“花费时间”;喜欢:
int counter = 0;
int nextPortBvalue = 0b00111111;
int LED0_timeOn = 0;
int LED1_timeOn = 1;
int LED2_timeOn = 2;
int LED3_timeOn = 3;
int LED4_timeOn = 4;
int LED5_timeOn = 0;
nextTick:
PORTB = nextPortBvalue;
__delay_ms(1);
counter++;
if(counter >= 4) {
counter = 0;
nextPortBvalue = 0b00111111;
}
if(counter == LED0_timeOn) {
nextPortBvalue &= ~0b00000001;
}
if(counter == LED1_timeOn) {
nextPortBvalue &= ~0b00000010;
}
if(counter == LED2_timeOn) {
nextPortBvalue &= ~0b00000100;
}
if(counter == LED3_timeOn) {
nextPortBvalue &= ~0b00001000;
}
if(counter == LED4_timeOn) {
nextPortBvalue &= ~0b00010000;
}
if(counter == LED5_timeOn) {
nextPortBvalue &= ~0b00100000;
}
goto nextTick;
这有点棘手 - 关闭一位(或一个 LED)而不弄乱它需要使用的其他位,并关闭除一位之外的所有其他位。 ~0b00000010
表示“反转这些位”,因此 ~0b00000010
变为 0b11111101
,而 nextPortBvalue &= ~0b00000010;
表示“nextPortBvalue
的新值与旧值相同AND 0b11111101"(导致第二位关闭,而其他所有位保持不变)。
虽然这有点混乱 - 它复制了 6 次逻辑(每个 LED 一次)。您可以通过使用数组和循环来解决该问题,例如:
int counter = 0;
int nextPortBvalue = 0b00111111;
int LED_timeOn[6] = { 0,1,2,3,4,0 };
nextTick:
PORTB = nextPortBvalue;
__delay_ms(1);
counter++;
if(counter >= 4) {
counter = 0;
nextPortBvalue = 0b00111111;
}
for(int bit = 0; bit < 6; bit++) {
if(counter == LED_timeOn[bit]) {
nextPortBvalue &= ~(0b00000001 << bit);
}
}
goto nextTick;
现在您可以控制每个 LED 开启(或关闭)的时间;你会想要改变 LED_timeOn
数组中的值的东西(并改变每个 LED 花费的时间)。您每 400 毫秒执行一次此操作,因此让我们添加第二个计数器来测量 400 毫秒:
int counter1 = 0;
int counter2 = 0;
int nextPortBvalue = 0b00111111;
int LED_timeOn[6] = { 0,0 };
nextTick:
PORTB = nextPortBvalue;
__delay_ms(1);
counter1++;
counter2++;
if(counter1 >= 4) {
counter1 = 0;
nextPortBvalue = 0b00111111;
}
if(counter2 >= 400) {
counter2 = 0;
}
for(int bit = 0; bit < 6; bit++) {
if(counter1 == LED_timeOn[bit]) {
nextPortBvalue &= ~(0b00000001 << bit);
}
}
goto nextTick;
每 400 毫秒,您希望所有现有的“LED 花费的时间”减少(因此 LED 会变暗),然后设置一个新的“100% 开启”LED。您需要另一个变量来跟踪哪个 LED 是下一个“100% 亮”的 LED。结果是这样的:
int counter1 = 0;
int counter2 = 0;
int nextPortBvalue = 0b00111111;
int LED_timeOn[6] = { 0,0 };
int nextOnLED = 0;
nextTick:
PORTB = nextPortBvalue;
__delay_ms(1);
counter1++;
counter2++;
if(counter1 >= 4) {
counter1 = 0;
nextPortBvalue = 0b00111111;
}
if(counter2 >= 400) {
counter2 = 0;
/* Make all the LEDs fade */
for(int bit = 0; bit < 6; bit++) {
if(LED_timeOn[bit] > 0) {
LED_timeOn[bit]--;
}
}
/* Change the value for the next "100% on" LED to 4 */
LED_timeOn[nextOnLED] = 4;
}
for(int bit = 0; bit < 6; bit++) {
if(counter1 == LED_timeOn[bit]) {
nextPortBvalue &= ~(0b00000001 << bit);
}
}
goto nextTick;
最后一个问题是您还想更改哪个 LED 是下一个“100% 亮”的 LED;但有时是左边的下一个 LED,有时是右边的下一个 LED。一种处理方法是假装中间有 4 个假 LED;这样在打开第 6 个 LED 后,您将打开第 7 个“假 LED”(实际上又是第 5 个 LED!)。这可能是这样的:
int counter1 = 0;
int counter2 = 0;
int nextPortBvalue = 0b00111111;
int LED_timeOn[6] = { 0,0 };
int nextOnLED = 0;
nextTick:
PORTB = nextPortBvalue;
__delay_ms(1);
counter1++;
counter2++;
if(counter1 >= 4) {
counter1 = 0;
nextPortBvalue = 0b00111111;
}
if(counter2 >= 400) {
counter2 = 0;
/* Make all the LEDs fade */
for(int bit = 0; bit < 6; bit++) {
if(LED_timeOn[bit] > 0) {
LED_timeOn[bit]--;
}
}
/* Change the value for the next "100% on" LED to 4 */
if(nextOnLED < 6) {
LED_timeOn[nextOnLED] = 4; /* A real LED */
} else {
LED_timeOn[10 - nextOnLED] = 4; /* A fake LED */
}
/* Update the next 100% on LED */
nextOnLED++;
if(nextOnLED >= 10) {
nextOnLED = 0;
}
}
for(int bit = 0; bit < 6; bit++) {
if(counter1 == LED_timeOn[bit]) {
nextPortBvalue &= ~(0b00000001 << bit);
}
}
goto nextTick;