|
8 |
PICによるLEDドライバボードの設計 (5) |
Geminiに設計してもらったプログラムをClaudeに修正してもらったものを以下に示します。
/* ============================================================
* NY_night -- PIC12F1501 LED driver
* rev.3 : SMD / PCBA board (AO3400A output stage) + sleep
*
* RA2 = PWM1 -> R2 -> T2 gate -> L_N (Left channel)
* RA4 = PWM3 -> R1 -> T1 gate -> R_N (Right channel)
* RA5 = mode button (SW1, to GND, internal pull-up)
* RA3 = unused input, brought out to JP1 pin 1 (MCLR/VPP)
*
* Modes: 1 alternating fade -> 2 steady -> 3 blink -> SLEEP -> 1 ...
* In sleep the core is stopped; a button press wakes it via IOC.
* ============================================================ */
#pragma config FOSC = INTOSC /* internal oscillator */
#pragma config WDTE = OFF /* watchdog off */
#pragma config PWRTE = ON /* power-up timer on */
#pragma config MCLRE = OFF /* pin 4 is RA3, not MCLR */
#pragma config CP = OFF /* code protect off */
#pragma config BOREN = NSLEEP /* BOR active when running, off asleep */
#pragma config CLKOUTEN = OFF /* no clock output */
#pragma config BORV = LO /* low BOR threshold (battery) */
#pragma config LPBOR = OFF /* low-power BOR off */
#pragma config LVP = OFF /* HV programming via JP1 */
#include
#include
#define _XTAL_FREQ 4000000
/* ---- timing, all expressed in 10 ms ticks ---- */
#define TICK_MS 10
#define FADE_TICKS 4 /* 40 ms per fade step */
#define BLINK_TICKS 30 /* 300 ms per blink phase */
#define DEBOUNCE_TICKS 3 /* 30 ms settle time */
#define FADE_STEP 5
#define DUTY_MAX 250
#define DUTY_MIN 5
#define PWM_L PWM1DCH /* RA2, pin 5 */
#define PWM_R PWM3DCH /* RA4, pin 3 */
#define SW_PRESSED (RA5 == 0)
/* ------------------------------------------------------------
* Output stage on / off
* With the PWM modules disabled the pins revert to port
* control; LATA = 0 holds both gates low, and R3/R4 back
* that up so the MOSFETs cannot float on.
* ---------------------------------------------------------- */
static void outputs_off(void)
{
PWM_L = 0;
PWM_R = 0;
PWM1CON = 0x00;
PWM3CON = 0x00;
T2CONbits.TMR2ON = 0;
LATA = 0x00;
}
static void outputs_on(void)
{
LATA = 0x00;
PWM_L = 0;
PWM_R = 0;
PR2 = 255;
T2CONbits.TMR2ON = 1;
PWM1CON = 0xC0;
PWM3CON = 0xC0;
}
/* ------------------------------------------------------------
* Sleep until the button is pressed.
* GIE stays 0, so the core resumes at the instruction after
* SLEEP() instead of vectoring to an ISR -- no ISR needed.
* ---------------------------------------------------------- */
static void sleep_until_button(void)
{
outputs_off();
IOCAF = 0x00; /* clear any stale edge flag */
IOCANbits.IOCAN5 = 1; /* wake on RA5 falling edge */
INTCONbits.IOCIE = 1;
INTCONbits.GIE = 0;
SLEEP();
NOP();
IOCANbits.IOCAN5 = 0;
INTCONbits.IOCIE = 0;
IOCAF = 0x00;
outputs_on();
}
int main(void)
{
/* ---------------- initialisation ---------------- */
OSCCON = 0x6A; /* 4 MHz internal */
ANSELA = 0x00; /* all pins digital */
LATA = 0x00; /* outputs low before they are enabled */
TRISA = 0b00101000; /* RA5 = button in, RA3 = input only */
WPUA = 0b00101000; /* pull up RA5 AND RA3 */
OPTION_REGbits.nWPUEN = 0; /* enable weak pull-ups globally */
T2CON = 0x04; /* Timer2 on, prescale 1:1 */
outputs_on(); /* PWM period 3.9 kHz, both channels 0 */
/* ---------------- state ---------------- */
uint8_t mode = 1; /* 0:sleep 1:fade 2:steady 3:blink */
uint8_t duty = 0;
int8_t step = FADE_STEP;
uint8_t fade_t = 0;
uint8_t blink_t = 0;
uint8_t blink_on = 1;
uint8_t sw_raw = 0; /* 1 = pressed */
uint8_t sw_prev = 0;
uint8_t sw_state = 0; /* debounced */
uint8_t dbnc = DEBOUNCE_TICKS;
while (1) {
/* -------- button: non-blocking, acts on release -------- */
sw_raw = SW_PRESSED ? 1 : 0;
if (sw_raw != sw_prev) {
dbnc = 0; /* level changed, restart timer */
} else if (dbnc < DEBOUNCE_TICKS) {
if (++dbnc == DEBOUNCE_TICKS) { /* level has settled */
if (sw_state == 1 && sw_raw == 0) {
mode = (uint8_t)((mode + 1) & 0x03);
duty = 0;
step = FADE_STEP;
fade_t = 0;
blink_t = 0;
blink_on = 1;
}
sw_state = sw_raw;
}
}
sw_prev = sw_raw;
/* -------- mode output -------- */
switch (mode) {
case 0: /* off -> sleep */
sleep_until_button();
/* Woken by a press. Go straight to mode 1 so that even a
* very short tap responds, then swallow this press so the
* release does not advance the mode a second time. */
mode = 1;
while (SW_PRESSED) {
__delay_ms(TICK_MS);
}
__delay_ms(30); /* release bounce */
sw_raw = sw_prev = sw_state = 0;
dbnc = DEBOUNCE_TICKS;
duty = 0;
step = FADE_STEP;
fade_t = 0;
blink_t = 0;
blink_on = 1;
break;
case 1: /* alternating fade */
if (++fade_t >= FADE_TICKS) {
fade_t = 0;
if (step > 0) {
if (duty < DUTY_MAX) duty = (uint8_t)(duty + FADE_STEP);
else step = -FADE_STEP;
} else {
if (duty > DUTY_MIN) duty = (uint8_t)(duty - FADE_STEP);
else step = FADE_STEP;
}
}
PWM_L = duty;
PWM_R = (uint8_t)(255 - duty);
break;
case 2: /* steady on */
PWM_L = 255;
PWM_R = 255;
break;
case 3: /* simultaneous blink */
if (++blink_t >= BLINK_TICKS) {
blink_t = 0;
blink_on ^= 1;
}
PWM_L = blink_on ? 255 : 0;
PWM_R = blink_on ? 255 : 0;
break;
}
__delay_ms(TICK_MS);
}
}