summaryrefslogtreecommitdiffstats
path: root/fw
diff options
context:
space:
mode:
Diffstat (limited to 'fw')
-rw-r--r--fw/ds18b20.c156
-rw-r--r--fw/pwm.c53
-rw-r--r--fw/timer0_test.c73
3 files changed, 282 insertions, 0 deletions
diff --git a/fw/ds18b20.c b/fw/ds18b20.c
new file mode 100644
index 0000000..0b57732
--- /dev/null
+++ b/fw/ds18b20.c
@@ -0,0 +1,156 @@
+#include <pic16f84a.h>
+
+typedef unsigned char uint8_t;
+
+__sfr __at(0x70) UART_TX;
+__sfr __at(0x72) UART_STATUS;
+
+__sfr __at(0x73) OW_DATA;
+__sfr __at(0x74) OW_CMD;
+__sfr __at(0x75) OW_STATUS;
+
+#define OW_RESET 0x00
+#define OW_WRITE 0x01
+#define OW_READ 0x02
+
+#define OW_BUSY 0x01
+#define OW_PRESENCE 0x02
+
+#define CMD_SKIP_ROM 0xCC
+#define CMD_CONVERT_T 0x44
+#define CMD_READ_SCRATCH 0xBE
+
+/*
+ * Fractional nibble lookup: each step is 0.0625 deg C.
+ * Tables give the two decimal digits (tenths, hundredths) truncated.
+ * e.g. frac=1 -> 0.0625 -> prints ".06"
+ * frac=8 -> 0.5000 -> prints ".50"
+ */
+static const uint8_t frac_tens[16] = {0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9};
+static const uint8_t frac_hund[16] = {0,6,2,8,5,1,7,4,0,6,2,8,5,1,7,3};
+
+/*
+ * Timer0 delay. OPTION_REG 0x07: T0CS=0 (Fosc/4), PSA=0, PS=111 (1:256).
+ * At 50 MHz: overflow every 256*256 / (50M/4) = 5.24 ms.
+ * 143 overflows ≈ 750 ms.
+ */
+#define STATUS_RP0 0x20
+#define T0IF 0x04
+
+static void wait_overflows(uint8_t n) {
+ INTCON &= ~T0IF;
+ while (n--) {
+ while (!(INTCON & T0IF));
+ INTCON &= ~T0IF;
+ }
+}
+
+static void delay_750ms(void) { wait_overflows(143); }
+
+static void uart_send(uint8_t b) {
+ while (UART_STATUS & 0x01);
+ UART_TX = b;
+}
+
+static void uart_puts(const char *s) {
+ while (*s) uart_send((uint8_t)*s++);
+}
+
+static void ow_wait(void) {
+ while (OW_STATUS & OW_BUSY);
+}
+
+static void ow_write_byte(uint8_t b) {
+ OW_DATA = b;
+ OW_CMD = OW_WRITE;
+ ow_wait();
+}
+
+static uint8_t ow_read_byte(void) {
+ OW_CMD = OW_READ;
+ ow_wait();
+ return OW_DATA;
+}
+
+static void print_uint8(uint8_t v) {
+ uint8_t started = 0;
+ if (v >= 200) { uart_send('2'); v -= 200; started = 1; }
+ else if (v >= 100) { uart_send('1'); v -= 100; started = 1; }
+ {
+ uint8_t t = '0';
+ while (v >= 10) { v -= 10; t++; }
+ if (started || t != '0') uart_send(t);
+ }
+ uart_send(v + '0');
+}
+
+void main(void) {
+ uint8_t lo, hi, sign, temp_int, frac;
+ uint8_t i;
+
+ /* Configure Timer0: bank 1, OPTION_REG = 0x07 (Fosc/4, prescaler 1:256) */
+ STATUS |= STATUS_RP0;
+ TMR0 = 0x07;
+ STATUS &= ~STATUS_RP0;
+
+ uart_puts("\x1B[2J\x1B[H");
+ uart_puts("DS18B20 Temperature\r\n");
+ uart_puts("-------------------\r\n");
+
+ while (1) {
+ /* Reset */
+ OW_CMD = OW_RESET;
+ ow_wait();
+ if (!(OW_STATUS & OW_PRESENCE)) {
+ uart_puts("No sensor\r\n");
+ delay_750ms();
+ continue;
+ }
+
+ /* Start temperature conversion */
+ ow_write_byte(CMD_SKIP_ROM);
+ ow_write_byte(CMD_CONVERT_T);
+ delay_750ms();
+
+ /* Reset and read scratchpad */
+ OW_CMD = OW_RESET;
+ ow_wait();
+ if (!(OW_STATUS & OW_PRESENCE)) {
+ uart_puts("Lost sensor\r\n");
+ continue;
+ }
+ ow_write_byte(CMD_SKIP_ROM);
+ ow_write_byte(CMD_READ_SCRATCH);
+
+ lo = ow_read_byte(); /* scratchpad byte 0: temp LSB */
+ hi = ow_read_byte(); /* scratchpad byte 1: temp MSB */
+ for (i = 0; i < 7; i++) ow_read_byte(); /* discard remaining bytes */
+
+ /*
+ * DS18B20 12-bit format:
+ * sign = hi bit 7 (and sign-extended through hi[7:4])
+ * integer part = hi[3:0]:lo[7:4]
+ * fractional nibble = lo[3:0] (each bit = 0.0625 deg C)
+ *
+ * Negative values are two's complement of the 16-bit word.
+ */
+ sign = hi & 0x80;
+ if (sign) {
+ lo = ~lo;
+ hi = ~hi;
+ if (lo == 0xFF) { lo = 0; hi++; }
+ else { lo++; }
+ }
+
+ temp_int = ((hi & 0x0F) << 4) | (lo >> 4);
+ frac = lo & 0x0F;
+
+ uart_puts("\rTemp: ");
+ if (sign) uart_send('-');
+ print_uint8(temp_int);
+ uart_send('.');
+ uart_send('0' + frac_tens[frac]);
+ uart_send('0' + frac_hund[frac]);
+ uart_puts(" C\r\n");
+ }
+}
diff --git a/fw/pwm.c b/fw/pwm.c
new file mode 100644
index 0000000..60f8b5c
--- /dev/null
+++ b/fw/pwm.c
@@ -0,0 +1,53 @@
+#include <pic16f84a.h>
+
+typedef unsigned char uint8_t;
+
+/*
+ * Breathing LED via bit-bang PWM on PORTA(0) and PORTB(0).
+ *
+ * The PWM period is 256 counts of the inner counter 'pwm'.
+ * Each duty level is held for HOLD_PERIODS full PWM periods before
+ * stepping, giving a smooth fade up then down (heartbeat pattern).
+ *
+ * At 50 MHz with SDCC's output (~10 instructions/iteration) the inner
+ * loop runs at roughly 5 MHz, so one 256-count PWM period ≈ 51 µs
+ * → ~20 kHz PWM. With HOLD_PERIODS = 32 each brightness step lasts
+ * ~1.6 ms and the full 0→255→0 sweep takes ~820 ms — a natural breath.
+ * Tweak HOLD_PERIODS to taste.
+ */
+
+#define HOLD_PERIODS 32
+
+void main(void) {
+ uint8_t duty = 0; /* current duty cycle, 0..255 */
+ uint8_t dir = 1; /* 1 = ramping up, 0 = ramping down */
+ uint8_t pwm = 0; /* free-running counter within one period */
+ uint8_t periods = 0; /* how many full periods at this duty level */
+
+ while (1) {
+ /* PWM compare: LED on when we're inside the duty window */
+ if (pwm < duty) {
+ PORTA = 0x01;
+ PORTB = 0x01;
+ } else {
+ PORTA = 0x00;
+ PORTB = 0x00;
+ }
+
+ pwm++; /* wraps 255→0, completing one 256-count PWM period */
+
+ if (pwm == 0) {
+ periods++;
+ if (periods == HOLD_PERIODS) {
+ periods = 0;
+ if (dir) {
+ duty++;
+ if (duty == 255) dir = 0; /* peak: start ramping down */
+ } else {
+ duty--;
+ if (duty == 0) dir = 1; /* floor: start ramping up */
+ }
+ }
+ }
+ }
+}
diff --git a/fw/timer0_test.c b/fw/timer0_test.c
new file mode 100644
index 0000000..ef5e622
--- /dev/null
+++ b/fw/timer0_test.c
@@ -0,0 +1,73 @@
+#include <pic16f84a.h>
+
+typedef unsigned char uint8_t;
+
+__sfr __at(0x70) UART_TX;
+__sfr __at(0x72) UART_STATUS;
+
+/*
+ * OPTION_REG 0x07: T0CS=0 (Fosc/4), PSA=0, PS=111 (1:256)
+ * At 50 MHz: overflow every 256*256 / (50M/4) = 5.24 ms → ~191 ovf/sec
+ */
+#define STATUS_RP0 0x20
+#define T0IF 0x04 /* INTCON bit 2 */
+#define T0IE 0x20 /* INTCON bit 5 */
+#define GIE 0x80 /* INTCON bit 7 */
+#define OVF_PER_SEC 191
+
+static volatile uint8_t ovf_count = 0;
+
+void isr(void) __interrupt(0) {
+ if (INTCON & T0IF) {
+ ovf_count++;
+ INTCON &= ~T0IF;
+ }
+}
+
+static void uart_send(uint8_t b) {
+ while (UART_STATUS & 0x01);
+ UART_TX = b;
+}
+
+static void uart_puts(const char *s) {
+ while (*s) uart_send((uint8_t)*s++);
+}
+
+static void print_uint8(uint8_t v) {
+ uint8_t started = 0;
+ if (v >= 200) { uart_send('2'); v -= 200; started = 1; }
+ else if (v >= 100) { uart_send('1'); v -= 100; started = 1; }
+ {
+ uint8_t t = '0';
+ while (v >= 10) { v -= 10; t++; }
+ if (started || t != '0') uart_send(t);
+ }
+ uart_send(v + '0');
+}
+
+void main(void) {
+ uint8_t seconds = 0;
+
+ /* Configure OPTION_REG (bank 1, same 7-bit addr as TMR0) */
+ STATUS |= STATUS_RP0;
+ TMR0 = 0x07;
+ STATUS &= ~STATUS_RP0;
+
+ TMR0 = 0;
+ INTCON = T0IE | GIE; /* enable Timer0 overflow interrupt */
+
+ uart_puts("\x1B[2J\x1B[H");
+ uart_puts("Timer0 test\r\n");
+ uart_puts("-----------\r\n");
+
+ while (1) {
+ ovf_count = 0;
+ while (ovf_count < OVF_PER_SEC); /* ISR bumps ovf_count */
+
+ uart_puts("t = ");
+ print_uint8(seconds);
+ uart_puts(" s\r\n");
+ PORTA ^= 0x01;
+ seconds++;
+ }
+}