LGT8F684P 軟串口輸入控制WS2812 LED
本帖最后由 eddiewwm 于 2019-2-7 23:01 编辑LGT群友 Zyleon 的一個LGT8F684P 軟串口輸入控制WS2812 LED範例:
#include <pic.h>
#include "lgt8f684p.h"
#define _XTAL_FREQ 16000000 * 2 // 16MHz, (4 / 2T) = 2
//#define ws2812_pin RC0
#define rx_pin RA6
bit rx_en;
unsigned char rx_recieve;
unsigned char rx_recieve_count = 8;
unsigned char recieved_data;
unsigned char rx_GRB;
unsigned char ws2812_prepared;
const unsigned char hue_ring[] @0x200 = {0, 255, 0, 64, 255, 0, 128, 255, 0, 192, 255, 0, 255, 255, 0, 255, 192, 0, 255, 128, 0, 255, 64, 0, 255, 0, 0, 255, 0, 64, 255, 0, 128, 255, 0, 192, 255, 0, 255, 192, 0, 255, 128, 0, 255, 64, 0, 255, 0, 0, 255, 0, 64, 255,0, 128, 255, 0, 192, 255, 0, 255, 255, 0, 255, 192, 0, 255, 128, 0, 255, 64};
void RXInit(void);
void ws2812Init(void);
void ws2812Send(unsigned char unused, unsigned char G, unsigned char R, unsigned char B);
void RXInit(void)
{
ANSEL = 0x00; // IO pins digital mode
TRISA |= 1 << 6; // Set rx_pin(RA6) to input
WPUA |= 1 << 6; // Pull-up rx_pin(RA6)
OSCCON = 0x71; // Set frequency 1/1 prescaler
OPTION_REG = 0x02; // Set Timer 0 1/8 prescaler
CMCON0 = 0x07; // Disable internal comparator
BGEN = 1; // Disable internal reference voltage
ADCON1 = 3; // Set differential amplification sampling
__delay_ms(100);
INTCON = 0xC8; // Enable global/peripheral/RA interrupt
IOCA |= 1 << 6; // Enable rx_pin(RA6) level change interrupt
}
void ws2812Init(void)
{
ANSEL = 0x00;
TRISC = 0;
PORTC = 0;
rx_GRB = 0;
ws2812_prepared = 0;
}
void ws2812Send(unsigned char unused, unsigned char G, unsigned char R, unsigned char B)
{
unsigned char i = 0;
#asm
bcf STATUS, 5
movlw 8;
movwf ws2812Send@i;
ws2812SetGreen:
bsf 7, 0;
btfsc ws2812Send@G, 7;
goto $+3;
nop;
bcf 7, 0;
goto $+1;
bcf 7, 0;
rlf ws2812Send@G, f;
decfsz ws2812Send@i, f;
goto ws2812SetGreen;
movlw 8;
movwf ws2812Send@i;
ws2812SetRed:
bsf 7, 0;
btfsc ws2812Send@R, 7;
goto $+3;
nop;
bcf 7, 0;
goto $+1;
bcf 7, 0;
rlf ws2812Send@R, f;
decfsz ws2812Send@i, f;
goto ws2812SetRed;
movlw 8;
movwf ws2812Send@i;
ws2812SetBlue:
bsf 7, 0;
btfsc ws2812Send@B, 7;
goto $+3;
nop;
bcf 7, 0;
goto $+1;
bcf 7, 0;
rlf ws2812Send@B, f;
decfsz ws2812Send@i, f;
goto ws2812SetBlue;
#endasm
}
void main(void)
{
RXInit();
GIE = 0;
ws2812Init();
ws2812Send(0, 128, 128, 128);
__delay_ms(1000);
ws2812Send(0, 255, 0, 0);
__delay_ms(100);
ws2812Send(0, 0, 255, 0);
__delay_ms(100);
ws2812Send(0, 0, 0, 255);
__delay_ms(100);
for (unsigned char i = 0; i < 5; i++)
{
for (unsigned char j = 0; j < 72; j += 3)
{
ws2812Send(0, hue_ring, hue_ring, hue_ring);
__delay_ms(30);
}
}
for (unsigned char i = 0; i < 200; i += 8)
{
ws2812Send(0, i, i, i);
__delay_ms(10);
}
GIE = 1;
while(1)
{
if (rx_en);
if (ws2812_prepared)
{
unsigned char G_rx = (recieved_data << 4) + recieved_data;
unsigned char R_rx = (recieved_data << 4) + recieved_data;
unsigned char B_rx = (recieved_data << 4) + recieved_data;
ws2812Send(0, G_rx, R_rx, B_rx);
ws2812_prepared = 0;
}
}
}
void interrupt pinLevelChange(void)
{
if (T0IE && T0IF)
{
T0IF = 0;
TMR0 = 256 - 104 + 2;
if (rx_recieve_count)
{
rx_recieve >>= 1;
if (rx_pin) rx_recieve |= 0x80;
rx_recieve_count--;
}
else
{
if ((rx_recieve >= '0') && (rx_recieve <= '9')) recieved_data = rx_recieve - '0';
else if ((rx_recieve >= 'A') && (rx_recieve <= 'F')) recieved_data = rx_recieve - 'A' + 10;
else recieved_data = 0;
if (++rx_GRB == 6)
{
ws2812_prepared = 1;
rx_GRB = 0;
}
rx_en = 0;
T0IE = 0;
IOCA |= 1 << 6; // Enable rx_pin(RA6) level change interrupt
}
}
else if (RAIF && (IOCA & 1 << 6) && (rx_pin == 0))
{
rx_en = 1;
IOCA &= ~(1 << 6); // Disable rx_pin(RA6) level change interrupt
TMR0 = 256 - 156 + 5;
T0IF = 0;
T0IE = 1;
rx_recieve_count = 8;
rx_recieve = 0;
}
else T0IE = 0;
T0IF = 0;
RAIF = 0;
}
上例在MPLAB IDE v8.92 運行下有報錯,更改以下後克服:
ws2812Send@i equ 0x72; //count buffer
bcf status, 5;
範例內的 ws2812 的驅力波形時序 時間也跟ws2812規格書要求("1":H0.85us+L0.4us,"0": H0.4us+L0.85us)有差異,現量出來的是:"1":H0.72us+L0.62us,"0": H0.5us+L1.0us。更改ws2812Send()內的asm結構,得到更接近的波形時序:"1":H0.76us+L0.52us,"0": H0.38us+L0.88us。
#include <pic.h>
#include "lgt8f684p.h"
#define _XTAL_FREQ 16000000 * 2 // 16MHz, (4 / 2T) = 2
//#define ws2812_pin RC0
#define rx_pin RA6
bit rx_en;
unsigned char rx_recieve;
unsigned char rx_recieve_count = 8;
unsigned char recieved_data;
unsigned char rx_GRB;
unsigned char ws2812_prepared;
const unsigned char hue_ring[] @0x200 = {0, 255, 0, 64, 255, 0, 128, 255, 0, 192, 255, 0, 255, 255, 0, 255, 192, 0, 255, 128, 0, 255, 64, 0, 255, 0, 0, 255, 0, 64, 255, 0, 128, 255, 0, 192, 255, 0, 255, 192, 0, 255, 128, 0, 255, 64, 0, 255, 0, 0, 255, 0, 64, 255, 0, 128, 255, 0, 192, 255, 0, 255, 255, 0, 255, 192, 0, 255, 128, 0, 255, 64};
void RXInit(void);
void ws2812Init(void);
void ws2812Send(unsigned char unused, unsigned char G, unsigned char R, unsigned char B);
void RXInit(void) {
ANSEL = 0x00; // IO pins digital mode
TRISA |= 1 << 6; // Set rx_pin(RA6) to input
WPUA |= 1 << 6; // Pull-up rx_pin(RA6)
OSCCON = 0x71; // Set frequency 1/1 prescaler
OPTION_REG = 0x02; // Set Timer 0 1/8 prescaler
CMCON0 = 0x07; // Disable internal comparator
BGEN = 1; // Disable internal reference voltage
ADCON1 = 3; // Set differential amplification sampling
__delay_ms(100);
INTCON = 0xC8; // Enable global/peripheral/RA interrupt
IOCA |= 1 << 6; // Enable rx_pin(RA6) level change interrupt
}
void ws2812Init(void) {
ANSEL = 0x00;
TRISC = 0;
PORTC = 0;
rx_GRB = 0;
ws2812_prepared = 0;
}
void ws2812Send(unsigned char unused, unsigned char G, unsigned char R, unsigned char B) {
unsigned char i = 0;
#asm
ws2812Send@i equ 0x72; //count buffer
bcf status, 5;
movlw 8;
movwf ws2812Send@i;
ws2812SetGreen:
//0.125us(1H6+1L4)==>0.75us+0.5us,
//0.125us(0H3+0L7)==>0.38us+0.88us
bsf 7, 0; //1H1, 0H1, 1
btfsc ws2812Send@G, 7; //1H2, 0H3, 1/2
goto $ + 2; //1H4, ---, 1
bcf 7, 0; //---, 0L1, 1
nop; //1H5, 0L2, 1
rlf ws2812Send@G, f; //1H6, 0L3, 1
bcf 7, 0; //1L1, 0L4, 1
decfsz ws2812Send@i, f;//1L2, 0L5, 1/2
goto ws2812SetGreen; //1L4, 0L7, 2
movlw 8;
movwf ws2812Send@i;
ws2812SetRed:
bsf 7, 0;
btfsc ws2812Send@R, 7;
goto $ + 2;
bcf 7, 0;
nop;
rlf ws2812Send@R, f;
bcf 7, 0;
decfsz ws2812Send@i, f;
goto ws2812SetRed;
movlw 8;
movwf ws2812Send@i;
ws2812SetBlue:
bsf 7, 0;
btfsc ws2812Send@B, 7;
goto $ + 2;
bcf 7, 0;
nop;
rlf ws2812Send@B, f;
bcf 7, 0;
decfsz ws2812Send@i, f;
goto ws2812SetBlue;
#endasm
}
void main(void) {
RXInit();
GIE = 0;
ws2812Init();
ws2812Send(0, 128, 128, 128);
__delay_ms(1000);
ws2812Send(0, 255, 0, 0);
__delay_ms(100);
ws2812Send(0, 0, 255, 0);
__delay_ms(100);
ws2812Send(0, 0, 0, 255);
__delay_ms(100);
for (unsigned char i = 0; i < 5; i++) {
for (unsigned char j = 0; j < 72; j += 3) {
ws2812Send(0, hue_ring, hue_ring, hue_ring);
__delay_ms(30);
}
}
for (unsigned char i = 0; i < 200; i += 8) {
ws2812Send(0, i, i, i);
__delay_ms(10);
}
GIE = 1;
while (1) {
if (rx_en);
if (ws2812_prepared) {
unsigned char G_rx = (recieved_data << 4) + recieved_data;
unsigned char R_rx = (recieved_data << 4) + recieved_data;
unsigned char B_rx = (recieved_data << 4) + recieved_data;
ws2812Send(0, G_rx, R_rx, B_rx);
ws2812_prepared = 0;
}
}
}
void interrupt pinLevelChange(void) {
if (T0IE && T0IF) {
T0IF = 0;
TMR0 = 256 - 104 + 2;
if (rx_recieve_count) {
rx_recieve >>= 1;
if (rx_pin) rx_recieve |= 0x80;
rx_recieve_count--;
} else {
if ((rx_recieve >= '0') && (rx_recieve <= '9')) recieved_data = rx_recieve - '0';
else if ((rx_recieve >= 'A') && (rx_recieve <= 'F')) recieved_data = rx_recieve - 'A' + 10;
else recieved_data = 0;
if (++rx_GRB == 6) {
ws2812_prepared = 1;
rx_GRB = 0;
}
rx_en = 0;
T0IE = 0;
IOCA |= 1 << 6; // Enable rx_pin(RA6) level change interrupt
}
} else if (RAIF && (IOCA & 1 << 6) && (rx_pin == 0)) {
rx_en = 1;
IOCA &= ~(1 << 6); // Disable rx_pin(RA6) level change interrupt
TMR0 = 256 - 156 + 5;
T0IF = 0;
T0IE = 1;
rx_recieve_count = 8;
rx_recieve = 0;
} else T0IE = 0;
T0IF = 0;
RAIF = 0;
}
串口 9600 baud,控制為6位碼 GGRRBB,當中GG是綠色的值(00~FF),RR是紅色的值(00~FF),BB是藍色的值(00~FF)。
页:
[1]