Code:
/*
File: main.c
Date: 06.02.2021
Author: Siro
Projekt: 10F322 RGB-Ansteuerung
18 LEDs für den K110 Hubschrauber
Einige Assembler Optimierungen vorgenommen
!!! PMDATH und PMDATL sind nicht initialisiert nach einem Reset
250ns Instruction Cycle, 4 MHz
Der Stripe mit 18 LEDs zieht knapp 100mA bei voller Helligkeit "einer" von 3 LEDs ROT oder GRÜN oder BLAU
Bei halber Helligkeit 0x7F nur ca. 50mA
RA3 low LED heller 0xFF
RA3 high LED dunkler 0x7F
Stripe mit 18 LEDs wiegt knapp 0,6 Gramm
Auch bei 1,65 Volt lief die Schaltung noch mit LEDs !!!!!
die blauen LEDs brauchen aber mindestens 2,5 Volt und sind dann noch recht dunkel
*/
// PIC10F322 Configuration Bit Settings
// 'C' source line config statements
// CONFIG
#pragma config FOSC = INTOSC // Oscillator Selection bits (INTOSC oscillator: CLKIN function disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
#pragma config LPBOR = OFF // Brown-out Reset Selection bits (BOR disabled)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#include <xc.h> // die einzige Headerdatei die benötigt wird.
// benötigen wir nicht, das Delay erzeugen wir selbst mittel Timer 0
// #define _XTAL_FREQ 16000000 // für 4 MHz instrction cylce time wegen der 4 stage pipeline
/*----------------------------------------------------------------------*/
/* hier habe ich meine eigenen Datentypen definiert: */
typedef unsigned char BOOL; /* 0=FALSE, all others are TRUE */
typedef unsigned char U8; /* 8 Bit ohne Vorzeichen */
typedef signed char S8;
typedef unsigned short U16; /* 16 Bit ohne Vorzeichen */
typedef unsigned long U32; /* 32 Bit ohne Vorzeichen */
/* dem Datentyp BOOL sollten nur diese Konstanten zugeordnet werden: */
#define FALSE (0) /* Eine 0 bedeutet FALSE (falsch) */
#define TRUE (!FALSE) /* alle anderen Werte bedeuten TRUE (wahr) */
// Die HARDWARE : 2 LEDs, 1 RGB Stripe WS2812 / SK68 kompatibel mit 18 LEDs
// LED 1 connected to pin RA0
#define LED1_ON LATA0=1
#define LED1_OFF LATA0=0
// LED 2 connected to pin RA1
#define LED2_ON LATA1=1
#define LED2_OFF LATA1=0
// Declaration einer einzelnen RGB LED
// Jede LED hat 3 Bytes, insgesamt also 24 Bits
typedef struct // __pack ? bei 8 Bittern unnötig ist immer packed
{
U8 green; /* 8 Bit fuer die Helligkeit gruen */
U8 red; /* 8 Bit fuer die Helligkeit rot */
U8 blue; /* 8 Bit fuer die Helligkeit blau */
} TLed; /* Type Bezeichner ist TLed */
#define LED_COUNT 18 // Anzahl der anzusteuernden RGB Leds max 18 ???
TLed LedArray[LED_COUNT]; // gesamtes Datenarray der Ledkette
// dieses Array wird dann per Assmblercode ausgeschoben
// U8 ArraySize = sizeof(LedArray); // die Groesse des Arrays in Bytes
// hier liegt eine Tabelle mit den ADU Werten für die entsprechende Spannung
// ADU ==> Voltage.
const U8 ADU_Table[] =
{
161, // 3,25
160, // 3,28
158, // 3,31
157, // 3,34
156, // 3.37
154, // 3,40
153, // 3,43
152, // 3,46
150, // 3,39
149, // 3,52
148, // 3,55
146, // 3,58
145, // 3,61
144, // 3,64
143, // 3,67
142, // 3,70
141, // 3,73
128 // 4,10
};
//------------------------------------------------------------------------------
// für 250ns Instruction Cycle Time 4 MHz
// PMDATL enthält das auszuschiebene Datenbyte für die WS2812 RGB-Led Kette
// wird also als Zwischenspeicher benutzt. (8 Bit Register)
// PMADRL wird für den Bytezähler benutzt (8 Bit Register)
// PMDATH wird als Bitzähler benutzt !!! ist nur ein 6 Bit Register
// das reicht aber, wir zählen ja nur von 8 bis 0
// !!! PMDATH ist nach reset nicht initialisiert, aber selbst mit dem Wert FF
// habe ich das optisch nicht wahrnehmen können.
void asm_LedShiftOut(void)
{
// asm ("clrf PMDATH"); // wegen optimierten bsf PMDATH,3 in der NextByte Loop
// asm ("incf PMDATH,F"); // testweise mal gucken
GIE=0; // global interrupt disable
asm("movlw LOW _LedArray"); // address of LedArray
asm("movwf FSR"); // to FSR index register
asm("movlw 18*3");
asm("movwf PMADRL"); // use PMADRL as byteCount
asm("Label_NextByte:"); // !! WICHTIG, der Doppelpunkt ab XC8 V2.20
asm("bsf PMDATH,3"); // PMDATH = 8
asm("movf INDF,W"); // load byte from LedArray
asm("movwf PMDATL"); // save Databyte to PMDATL
asm("Label_ByteLoop:");
asm("btfsc PMDATL,7"); // scip if databit is clear (MSB)
asm("goto Label_High"); // else databit ist set
asm("bsf LATA,2"); // set WS2812 DataLine to High 250ns
asm("bcf LATA,2"); // set WS2812 DataLine to Low
asm("goto Label_nextBit");
asm("Label_High:"); // High Databit
asm("bsf LATA,2"); // set WS2812 DataLine to High
asm("goto $+1");
asm("bcf LATA,2"); // set Line to Low
asm("Label_nextBit:");
asm("rlf PMDATL,F"); // databyte left
asm("decfsz PMDATH,F"); // bitCount-1, scip if all bits done, bit count -1
asm("goto Label_ByteLoop"); // next bit
asm("incf FSR,F"); // address pointer to next Led Byte
asm("decfsz PMADRL,F"); // byteCount-1 scip if all bytes done
asm("goto Label_NextByte"); // else send next byte
GIE=1; // global interrupt enable
// __delay_us(100); // Das Ende der Datenübertragung erreicht wenn die Leitung länger als 50us Low bleibt.
// !!! mindestens 80us bei SK68 Leds
// hier brauche ich eigentlich nicht warten, weil der restliche Programmcode benötigt ja schon die Zeit ???
// asm("movlw 0x85"); // das sind 100us
// asm("movwf PMDATL");
// asm("Label_delay100:");
// asm("decfsz PMDATL,F");
// asm("goto Label_delay100");
// asm("NOP"); // nur um Breakpoint zu setzen, Zeit messen
}
//------------------------------------------------------------------------------
// global interrupt function
// PWM1DCH wird runtergezählt wenn noch nicht 0
// PWM1DCH dient also als globale Zählvariable
void __interrupt() isr(void) // ORG 0x0004
{
TMR0 = 100; // restart timer 0 value for 10ms
TMR0IF = 0; // clear the timer 0 interrupt flag
if (PWM1DCH) // wenn die Zählvariable noch nicht 0 ist,
PWM1DCH--; // dann ziehen wir eins ab.
}
//------------------------------------------------------------------------------
// PWM1DCH wird im Timer 0 Interrupt alle 10ms um eins runtergezählt
// solange der Wert noch nicht 0 ist.
// !! RAM ist knapp:
// PWM1DCH wird als Zählvariable benutzt, ein 8 Bit Register welches wir nicht
// benötigen.
void Delay_10ms(U8 cnt)
{
PWM1DCH = cnt; // setzte Wert für 10ms * cnt
while (PWM1DCH) ; // warte bis count 0 wird.
}
//------------------------------------------------------------------------------
void FillGreen(void) // wird nicht benutzt
{ U8 i;
for (i=0; i<LED_COUNT; i++)
{
LedArray[i].red = 0x00;
LedArray[i].green = 0x7F;
LedArray[i].blue = 0x00;
}
}
void asm_FillGreen(void) // wird nicht benutzt
{
asm("movlw LOW _LedArray"); // address of LedArray
asm("movwf FSR"); // to FSR index register
asm("movlw 18"); // LED_COUNT Led Counter
asm("movwf PMADRL"); // use PMADRL as counter
asm("FillGreenLoop:");
asm("movlw 0x7F");
asm("movwf INDF"); // save byte to Array .green
asm("incf FSR,F");
asm("clrw");
asm("movwf INDF"); // save byte to Array .red
asm("incf FSR,F");
asm("movwf INDF"); // save byte to Array .blue
asm("incf FSR,F");
asm("decfsz PMADRL,F");
asm("goto FillGreenLoop");
}
void asm_FillRed(void)
{
asm("movlw LOW _LedArray"); // address of LedArray
asm("movwf FSR"); // to FSR index register
asm("movlw 18"); // LED_COUNT Led Counter
asm("movwf PMADRL"); // use PMADRL as counter
asm("FillRedLoop:");
asm("clrw");
asm("movwf INDF"); // save byte to Array .green
asm("incf FSR,F");
asm("movlw 0x7F");
asm("movwf INDF"); // save byte to Array .red
asm("incf FSR,F");
asm("clrw");
asm("movwf INDF"); // save byte to Array .blue
asm("incf FSR,F");
asm("decfsz PMADRL,F");
asm("goto FillRedLoop");
}
void asm_FillBlue(void)
{
asm("movlw LOW _LedArray"); // address of LedArray
asm("movwf FSR"); // to FSR index register
asm("movlw 18"); // LED_COUNT Led Counter
asm("movwf PMADRL"); // use PMADRL as counter
asm("FillBlueLoop:");
asm("clrw");
asm("movwf INDF"); // save byte to Array .green
asm("incf FSR,F");
asm("movwf INDF"); // save byte to Array .red
asm("incf FSR,F");
asm("movlw 0x7F");
asm("movwf INDF"); // save byte to Array .blue
asm("incf FSR,F");
asm("decfsz PMADRL,F");
asm("goto FillBlueLoop");
}
//------------------------------------------------------------------------------
void Calculate(void)
{ U8* p = (U8*)LedArray;
U8 i;
for (i=0; i < sizeof(ADU_Table); i++)
{
if (ADRES < ADU_Table[i]) // kleiner weil ADU Werte invertiert laufen
{
if (RA3) *p++=0x7F; // green
else *p++=0xFF; // more light if jumper RA3 is set
*p++=0x00; // red
*p++=0x00; // blue
} else
{
*p++=0x00; // green
if (RA3) *p++=0x7F; // red
else *p++=0xFF; // more light if jumper RA3 is set
*p++=0x00; // blue
}
}
}
//------------------------------------------------------------------------------
// internal FRC Conversion time measured 26,50µs for one conversion
// we calculate the average value over 128 measurements
// RAM ist knapp:
// Schleifenvariable i wird direkt im Register PMDATL ausgeführt
// Laufzeit ca. 3,8ms
void ReadAdu(void)
{ U16 value;
// LATA1 = 1; // testweise fuer die Zeitmessung
value = 0;
for (PMDATL=0; PMDATL<128; PMDATL++)
{
GO_nDONE = 1; // start ADU conversion
while (GO_nDONE) ; // wait until adu ready
value+=ADRES; // add the result
}
value >>=7; // divide by 128
ADRES=(U8)value; // write back the filtered ADU result
// LATA1 = 0; // testweise fuer die Zeitmessung
}
//------------------------------------------------------------------------------
void main(void)
{
PMDATH = 0; // ! register not initialized on reset
// used only in asm_LedShiftOut as bitcounter
OPTION_REG = 0x07; // prescale to timer 0, 1:256, enable pullups
// !!!! Pullps enabled with LOW on Bit 7
// WPUA = 0x08; // Pullup only on RA3, all pullups enable at power on
OSCCON = 0x70; // 16 MHz clock
INTCON = 0xF0; // enable global, peripheral, timer 0 interrupts
TRISA = 0x08; // all pins to output, RA3 is input only
// internal fixed voltage reference
// set first the FREN Bit before changing the gain bits
// see errata sheet
FVREN = 1; // Fixed Voltage Reference is enabled
ADFVR1 = 1; // select the 2,048 Volt Reference
// init the ADC
// ANSELA=1; AN0 as analog input, all others as digital output
// we dont need an ADU input, always convert the fixed voltage reference
ADCON = (0x07 << 5) // conversion clock internal FRC
// + (0x00 << 2) // select channel AN0, we dont need a channel
+ (0x07 << 2) // select Fixed Voltage Reference
+ (0x01 << 0); // ADC enable
while (1) // the main loop
{
ReadAdu(); // with average filter
if (ADRES > 161) // !! Abfrage invers < 3,25 Volt Unterspannung
{
asm_FillRed();
asm_LedShiftOut(); // RGB LEDs ausschieben
Delay_10ms(25);
LATA0 = 1; // erste LED Wechselblinker am Heck
LATA1 = 0; // zweite LED Wechselblinker am Heck
asm_FillBlue();
asm_LedShiftOut(); // RGB LEDs ausschieben
Delay_10ms(25);
LATA0 = 0; // erste LED Wechselblinker am Heck
LATA1 = 1; // zweite LED Wechselblinker am Heck
} else
{
Calculate(); // calculate the RGB Bargraph
asm_LedShiftOut();
Delay_10ms(2); // 20ms Anzeige
LATA0 = 1; // erste LED Wechselblinker am Heck
LATA1 = 1; // zweite LED Wechselblinker am Heck
} // else
} // while
} // main
Lesezeichen