// test_pwm.c /* Copyright (C) 2017 H.Poetzl ** ** This program is free software: you can redistribute it and/or ** modify it under the terms of the GNU General Public License ** as published by the Free Software Foundation, either version ** 2 of the License, or (at your option) any later version. */ // ------------------------------------------------ // configuration #define NO_BIT_DEFINES #include #include #define CONFIG(k, n) __code static char __at _ ## k __ ## k = n CONFIG(CONFIG1, _FEXTOSC_OFF & _RSTOSC_HFINT1 & _CSWEN_ON & _CLKOUTEN_OFF); CONFIG(CONFIG2, _MCLRE_ON & _WDTE_OFF & _PPS1WAY_OFF); #define CPU_NDIV 0 #define TMR2_PRE 2 #define TMR0_PRE (TMR2_PRE + CPU_NDIV + 5) static const uint8_t tmr0_val[] = { 0xA0, 0xA0, 0xA0, 0xA0 }; #include "tear_leds.h" /* ISR/asm related registers */ static volatile uint8_t __at (0x2100) seq[256]; __sfr __at (0x0F0) idx = 0; /* seq index */ __sfr __at (0x0F1) wrk = 0; /* seq index worker */ __sfr __at (0x0F2) bri = 0; /* test */ __sfr __at (0x0F3) tmp = 0; /* test */ #define IX(i) ((i) << 3) #define IA(i) (0x2100 + ((i) << 1)) #define WD(l,h) (((uint16_t)(h) << 8) | ((uint8_t)(l))) static const uint16_t seq_i[128] = { WD(IX(1), LED4A), 0x0000, codeX, IA(64), WD(IX(2), LED5A), 0x0000, codeX, IA(66), WD(IX(3), LED6A), 0x0000, codeX, IA(68), WD(IX(4), LED7A), 0x0000, codeX, IA(70), WD(IX(0), LED8A), 0x0000, codeX, IA(72), [64] = 0, 0x0700, [66] = 0, 0x0600, [68] = 0, 0x0500, [70] = 0, 0x0400, [72] = 0, 0x0300, }; #include "pwm_val.h" void irq(void) __interrupt 0 { __asm BANKSEL PIR4 BTFSC PIR4,CCP1IF BRA _ccp1 BANKSEL PIR0 BTFSC PIR0,TMR0IF BRA _tmr0 _out: RETFIE _tmr0: BCF PIR0,TMR0IF BANKSEL LEDBL BSF LEDBL,LEDB BCF LEDBL,LEDB ; BANKSEL T2CON ; BTFSC T2CON,TMR2ON ; BRA _ccp1 BANKSEL _idx MOVFW _idx ; use index MOVWF FSR0L BANKSEL TOSL MOVWF FSR0L_SHAD ; update FSR0L MOVIW 4[FSR0] ; code addr lsb MOVWF TOSL MOVIW 5[FSR0] ; code addr msb MOVWF TOSH MOVIW 6[FSR0] MOVWF FSR1L_SHAD ; update FSR1L MOVIW 7[FSR0] MOVWF FSR1H_SHAD ; update FSR1H ; BRA _out RETFIE _ccp1: BCF PIR4,CCP1IF BANKSEL T2CON BCF T2CON,TMR2ON BANKSEL _idx MOVFW _idx ; use index MOVWF _wrk ; save for worker MOVWF FSR0L MOVLW 0x0E ; pps bank 29 MOVWF FSR1H MOVIW 1[FSR0] ; this pps addr MOVWF FSR1L BCF INDF1,2 ; reset pps to GPIO MOVIW 0[FSR0] ; new index MOVWF _idx MOVWF FSR0L MOVIW 1[FSR0] ; next pps addr MOVWF FSR1L BSF INDF1,2 ; set pps to CLC1 BANKSEL CCPR1L MOVIW 2[FSR0] ; pwm lsbs MOVWF CCPR1L MOVIW 3[FSR0] ; pwm msbs MOVWF CCPR1H BANKSEL LEDCL BSF LEDCL,LEDC BCF LEDCL,LEDC ; BRA _out RETFIE __endasm; } // -------------------------------------------------- // and our main entry point void main() { // all digital ANSELA = 0; ANSELB = 0; ANSELC = 0; // all input TRISA = 0xFF; TRISB = 0xFF; TRISC = 0xFF; // all push pull ODCONA = 0x00; ODCONB = 0x00; ODCONC = 0x00; // all outputs '1' LATA = 0xFF; LATB = 0xFF; LATC = 0xFF; // reset PPS LED0P = 0; LED1P = 0; LED2P = 0; LED3P = 0; LED4P = 0; LED5P = 0; LED6P = 0; LED7P = 0; LED8P = 0; LED9P = 0; LEDAP = 0; LEDBP = 0; LEDCP = 0; LEDDP = 0; LEDEP = 0; LEDFP = 0; __asm BANKSEL TRISA BCF LED0T,LED0 BCF LED1T,LED1 BCF LED2T,LED2 BCF LED3T,LED3 BCF LED4T,LED4 BCF LED5T,LED5 BCF LED6T,LED6 BCF LED7T,LED7 BCF LED8T,LED8 BCF LED9T,LED9 BCF LEDAT,LEDA BCF LEDBT,LEDB BCF LEDCT,LEDC BCF LEDDT,LEDD BCF LEDET,LEDE BCF LEDFT,LEDF __endasm; __asm BANKSEL LATA BCF LED0L,LED0 BCF LED1L,LED1 BCF LED2L,LED2 BCF LED3L,LED3 BCF LED9L,LED9 BCF LEDAL,LEDA BCF LEDBL,LEDB BCF LEDCL,LEDC __endasm; /* configure oscillator */ OSCCON3bits.CSWHOLD = 0; OSCCON1bits.NDIV = CPU_NDIV; /* configure Timer 0 */ T0CON0 = 0; T0CON0bits.T016BIT = 0; T0CON0bits.T0OUTPS = 0b0000; /* Postscaler = 1 */ T0CON1 = 0; T0CON1bits.T0CS = 0b100; /* Source = LFINTOSC */ T0CON1bits.T0CS = 0b011; /* Source = HFINTOSC */ T0CON1bits.T0ASYNC = 1; T0CON1bits.T0CKPS = TMR0_PRE; TMR0H = tmr0_val[TMR2_PRE]; TMR0L = 0x00; /* configure Timer 2 */ T2CON = 0; T2CONbits.TMR2ON = 0; /* Timer is off */ T2CONbits.T2CKPS = TMR2_PRE; /* Prescaler */ T2CONbits.T2OUTPS = 0b0000; /* Postscaler = 1 */ PR2 = 0xFF; TMR2 = 0; /* configure CCP1 */ CCPTMRSbits.C1TSEL = 0b01; /* Based on Timer2 */ CCP1CON = 0; CCP1CONbits.CCP1MODE = 0b1111; /* PWM mode */ CCP1CONbits.CCP1FMT = 1; /* Left aligned */ CCP1CONbits.CCP1EN = 1; /* Enable CCP1 */ CCPR1H = 0x80; CCPR1L = 0x40; /* configure CCP2 */ CCPTMRSbits.C2TSEL = 0b01; /* Based on Timer2 */ CCP2CON = 0; CCP2CONbits.CCP2MODE = 0b1111; /* PWM mode */ CCP2CONbits.CCP2FMT = 1; /* Left aligned */ CCP2CONbits.CCP2EN = 0; /* Enable CCP1 */ CCPR2H = 0xF0; CCPR2L = 0x00; /* configure CLKR */ CLKRCON = 0; CLKRCONbits.CLKRDC = 0b10; /* 50% duty cycle */ CLKRCONbits.CLKRDIV = 0b010; /* FOSC/4 */ CLKRCONbits.CLKREN = 1; /* configure CLC */ CLC1SEL0bits.LC1D1S = 0b01100; /* CCP1 output */ CLC1GLS0 = 0b00000010; /* one true in */ CLC1GLS1 = 0; CLC1GLS2 = 0; CLC1GLS3 = 0; CLC1POL = 0b00000011; CLC1CONbits.LC1MODE = 0b000; /* AND - OR */ CLC1CONbits.LC1EN = 1; /* Enable LC1 */ /* disable unused modules */ PMD0 = 0b01000101; /* All but Clock Net,CLKR */ PMD1 = 0b11111010; /* All but Timer 0,2 */ PMD2 = 0b01100110; /* All */ PMD3 = 0b11111110; /* All but CCP1 */ PMD4 = 0b00100010; /* All */ PMD5 = 0b00011101; /* All but CLC1 */ /* PPS */ PPSLOCKbits.PPSLOCKED = 0; // LED9P = 0b01100; LED0P = 0b11110; /* clock on LED0 */ LED2P = 0b01100; /* CCP1 on LED2 */ LED3P = 0b00100; /* CLC1 on LED3 */ // LED6P = 0b00100; /* CLC1 on LED3 */ __asm BANKSEL _idx MOVLW 0xFF MOVWF _idx MOVLW high(_seq) MOVWF FSR0H MOVLW low(_seq) MOVWF FSR0L MOVLW high(_seq_i) MOVWF FSR1H MOVLW low(_seq_i) MOVWF FSR1L _copy: MOVIW FSR1++ MOVWI FSR0++ DECFSZ _idx,F BRA _copy MOVLW high(_seq) MOVWF FSR0H MOVLW low(_seq) MOVWF FSR0L BANKSEL CCPR1L MOVIW 2[FSR0] ; pwm lsbs MOVWF CCPR1L MOVIW 3[FSR0] ; pwm msbs MOVWF CCPR1H __endasm; PIR0bits.TMR0IF = 0; /* clear TMR0 irq */ PIE0bits.TMR0IE = 1; /* enable TMR0 irq */ PIR1bits.TMR2IF = 0; /* clear TMR2 irq */ PIE1bits.TMR2IE = 0; /* enable TMR2 irq */ PIR4bits.CCP1IF = 0; /* clear CCP1 irq */ PIE4bits.CCP1IE = 1; /* enable CCP1 irq */ INTCONbits.PEIE = 1; /* enable peripheral irq */ INTCONbits.GIE = 1; /* enable global irq */ T0CON0bits.T0EN = 1; while (1); } static inline void prep(void) { __asm BANKSEL TMR2 MOVLW 0xFF MOVWF TMR2 BANKSEL T2CON BSF T2CON,TMR2ON BANKSEL CCPR1L MOVFW CCPR1L ; check for zero PWM IORWF CCPR1H,W BANKSEL PIR4 BTFSC STATUS,2 ; skip if not zero PWM BSF PIR4,CCP1IF __endasm; } static inline void done(void) { __asm BANKSEL T2CON BTFSC T2CON,TMR2ON BRA $-1 SLEEP BRA $-1 __endasm; } static inline void pwm16(void) { __asm BANKSEL _bri MOVFW _bri CALL pwm_v16l MOVWI 2[FSR0] MOVFW _bri CALL pwm_v16h MOVWI 3[FSR0] __endasm; } static inline void pwm64(void) { __asm BANKSEL _bri MOVFW _bri CALL pwm_v64l MOVWI 2[FSR0] MOVFW _bri CALL pwm_v64h MOVWI 3[FSR0] __endasm; } #define CODE(val, off) do { \ prep(); \ \ if (INDF1 == 0) { /* init */ \ val = 0; \ INDF1 = 1; \ } \ \ val = (val + 1) & 0x7FF; \ bri = 0; \ \ if ((val >= off) && (val < off + 0x40)) \ bri = val; \ if ((val >= off + 0x40) && (val < off + 0x80)) \ bri = 63 - val; \ \ pwm64(); \ done(); \ } while(0) // #define RGET(idx,val) __asm__("MOVIW " #idx "[FSR1]\t;" #val) static void code0(void) { static uint16_t val; prep(); __asm BANKSEL LED9L BSF LED9L,LED9 __endasm; if (INDF1 == 0) { /* init */ val = 0; INDF1 = 1; } val = (val + 1) & 0x7FF; bri = 0; if ((val >= 0x100) && (val < 0x140)) bri = val; if ((val >= 0x140) && (val < 0x180)) bri = 63 - val; pwm64(); __asm BANKSEL LED9L BCF LED9L,LED9 __endasm; done(); } static void code1(void) { static uint16_t val; prep(); __asm BANKSEL LEDAL BSF LEDAL,LEDA __endasm; if (INDF1 == 0) { /* init */ val = 0; INDF1 = 1; } val = (val + 1) & 0x7FF; bri = 0; if ((val >= 0x200) && (val < 0x240)) bri = val; if ((val >= 0x240) && (val < 0x280)) bri = 63 - val; pwm64(); __asm BANKSEL LEDAL BCF LEDAL,LEDA __endasm; done(); } static void code2(void) { static uint16_t val; CODE(val, 0x300); } static void code3(void) { static uint16_t val; CODE(val, 0x400); } static void code4(void) { static uint16_t val; CODE(val, 0x500); } static void codeX(void) { prep(); __asm MOVFW INDF1 BZ _norm ; init CLRF INDF1 _norm: BANKSEL _bri CLRF _bri ; default off MOVIW 2[FSR1] ADDLW 2 MOVWI 2[FSR1] MOVIW 3[FSR1] ADDWFC INDF1,W ; always zero ANDLW 0x07 ; range MOVWI 3[FSR1] BNZ _nset MOVIW 2[FSR1] BTFSC WREG,7 SUBLW 0x80 LSRF WREG,W MOVWF _bri _nset: __endasm; pwm64(); done(); }