/* * The PIC32 twin's bare-metal fixture for the PIC32MX1xx/2xx, whose peripherals reach their * pins through peripheral pin select (PPS; PIC32 FRM Section 12.3.1, DS60001120F). Registers * only, built with clang (build.sh), for the PIC32MX250F128B: every address, IRQ, vector and PPS * code below is the part's, from its EDC file (DS60001168L Tables 7-1, 11-1, 11-2). * * UART1 at 115200, U1TX on RPB3 (RPB3R = 0001): the lines below; * RB13: toggled every 10000 core-timer counts by polling Count -- 1 kHz from Count's * SYSCLK/2 = 20 MHz; * RB14: toggled by the core timer's interrupt every 1 ms, priority 7 -- 500 Hz; * RB8: toggled by Timer2's interrupt, priority 3, PBCLK/8/20000 = 250 Hz periods -- 125 Hz; * OC1: PWM from Timer3 at 1 kHz, 25 %, on RPB7 (RPB7R = 0101) for the first 100 ms, then * moved to RPB15: RB7 stops, RB15 goes on. * * Then, once: INT4 read through PPS counts the rising edges RB5 makes through a jumper the * runner wires to RA0: none with INT4R = 0010 (RPB4), five of nine with 0000 (RPA0); change * notice by port -- * CNCONB.ON, CNENB.11 -- counts the four changes RB10 makes on RB11 through a jumper, each * cleared by reading PORTB; an analog pin (ANSELB.2 set at reset) held high reads 0 until its * ANSEL bit is cleared; RB4 reads its pull-up (CNPUB); with CFGCON.IOLOCK set (after the SYSKEY * unlock) a write to RPB15R changes nothing, and with DEVCFG3.IOL1WAY 1 IOLOCK does not clear. * * SPDX-License-Identifier: Apache-2.0 */ #include #define SFR(a) (*(volatile uint32_t *)(a)) #define CLR 1 #define SET 2 #define INV 3 #define REG(a, op) SFR((a) + 4 * (op)) #define U1MODE 0xBF806000u #define U1STA 0xBF806010u #define U1TXREG 0xBF806020u #define U1BRG 0xBF806040u #define T2CON 0xBF800800u #define TMR2 0xBF800810u #define PR2 0xBF800820u #define T3CON 0xBF800A00u #define TMR3 0xBF800A10u #define PR3 0xBF800A20u #define OC1CON 0xBF803000u #define OC1R 0xBF803010u #define OC1RS 0xBF803020u #define INTCON 0xBF881000u #define IFS0 0xBF881030u #define IFS1 0xBF881040u #define IEC0 0xBF881060u #define IEC1 0xBF881070u #define IPC0 0xBF881090u #define IPC2 0xBF8810B0u #define IPC4 0xBF8810D0u #define IPC8 0xBF881110u #define ANSELA 0xBF886000u #define TRISA 0xBF886010u #define ANSELB 0xBF886100u #define TRISB 0xBF886110u #define PORTB 0xBF886120u #define LATB 0xBF886130u #define CNPUB 0xBF886150u #define CNCONB 0xBF886170u #define CNENB 0xBF886180u #define CNSTATB 0xBF886190u #define CFGCON 0xBF80F200u #define DEVID 0xBF80F220u #define SYSKEY 0xBF80F230u #define OSCCON 0xBF80F000u #define INT4R 0xBF80FA10u #define RPB3R 0xBF80FB38u #define RPB7R 0xBF80FB48u #define RPB15R 0xBF80FB68u static inline uint32_t mfc0_count(void) { uint32_t v; __asm__ volatile("mfc0 %0, $9" : "=r"(v)); return v; } static inline void mtc0_compare(uint32_t v) { __asm__ volatile("mtc0 %0, $11" : : "r"(v)); } /* Busy-wait n counts of Count (20 MHz). */ static void wait_counts(uint32_t n) { uint32_t t = mfc0_count(); while (mfc0_count() - t < n) { } } static void putc_(char c) { while (SFR(U1STA) & (1u << 9)) { /* UTXBF */ } SFR(U1TXREG) = (uint8_t)c; } static void puts_(const char *s) { while (*s) { putc_(*s++); } } static void put_hex(uint32_t v) { puts_("0x"); for (int i = 28; i >= 0; i -= 4) { putc_("0123456789abcdef"[(v >> i) & 0xf]); } } static void put_dec(uint32_t v) { char b[11]; int n = 0; do { b[n++] = '0' + v % 10; v /= 10; } while (v); while (n) { putc_(b[--n]); } } static void unlock(void) { SFR(SYSKEY) = 0; SFR(SYSKEY) = 0xAA996655u; SFR(SYSKEY) = 0x556699AAu; } static volatile uint32_t ct_ticks, t2_ticks, ct_next, int4_hits, cn_hits; void ct_handler(void) { ct_ticks++; ct_next += 20000; /* 1 ms at 20 MHz */ mtc0_compare(ct_next); REG(IFS0, CLR) = 1u << 0; /* CTIF */ REG(LATB, INV) = 1u << 14; /* RB14 */ } void t2_handler(void) { t2_ticks++; REG(IFS0, CLR) = 1u << 9; /* T2IF: IRQ 9 */ REG(LATB, INV) = 1u << 8; /* RB8 */ } void int4_handler(void) { int4_hits++; REG(IFS0, CLR) = 1u << 23; /* INT4IF: IRQ 23 */ } /* Reading PORTB ends the mismatch; then the flag is cleared (FRM 12.3.3.1). */ void cn_handler(void) { (void)SFR(PORTB); cn_hits++; REG(IFS1, CLR) = 1u << 14; /* CNBIF: IRQ 46 */ } void exc_handler(void) { uint32_t cause, epc; __asm__ volatile("mfc0 %0, $13" : "=r"(cause)); __asm__ volatile("mfc0 %0, $14" : "=r"(epc)); puts_("EXC cause="); put_hex(cause); puts_(" epc="); put_hex(epc); puts_("\n"); for (;;) { } } static void once(void) { uint32_t held, digital, nopull, pullup, before, after, lock, unlocked, elsewhere; /* * INT4 through PPS: RB5 drives RA0 through the jumper. With INT4R 0010 (RPB4, group 1) * INT4 is on RB4, which nothing drives, and counts none of RB5's edges; with INT4R 0000 * (RPA0, also the reset value) it counts its five rising edges of nine. */ REG(ANSELA, CLR) = 1u << 0; SFR(INT4R) = 2; REG(INTCON, SET) = 1u << 4; /* INT4EP: rising */ REG(IPC4, SET) = 2u << 26; /* vector 19: IPC4<28:26> */ REG(TRISB, CLR) = 1u << 5; REG(LATB, CLR) = 1u << 5; REG(IFS0, CLR) = 1u << 23; REG(IEC0, SET) = 1u << 23; for (int pass = 0; pass < 2; pass++) { if (pass) { elsewhere = int4_hits; REG(LATB, CLR) = 1u << 5; wait_counts(20000); SFR(INT4R) = 0; } for (int i = 0; i < 9; i++) { if (i & 1) { REG(LATB, CLR) = 1u << 5; } else { REG(LATB, SET) = 1u << 5; } wait_counts(20000); } } puts_("INT4 via RPB4 rising="); put_dec(elsewhere); puts_(" via RPA0 rising="); put_dec(int4_hits - elsewhere); puts_("\n"); /* Change notice on port B: RB11 enabled, RB10 drives it through the jumper. */ REG(TRISB, CLR) = 1u << 10; REG(LATB, CLR) = 1u << 10; SFR(CNCONB) = 1u << 15; /* ON, mismatch mode */ SFR(CNENB) = 1u << 11; (void)SFR(PORTB); /* latch the inputs */ REG(IPC8, SET) = 2u << 18; /* vector 34: IPC8<20:18> */ REG(IFS1, CLR) = 1u << 14; REG(IEC1, SET) = 1u << 14; for (int i = 0; i < 4; i++) { REG(LATB, INV) = 1u << 10; wait_counts(20000); } REG(IEC1, CLR) = 1u << 14; puts_("CN port B RB11 changes="); put_dec(cn_hits); puts_("\n"); /* RB2 (AN4) held high by the runner: analog at reset, it reads 0 (12.3.4). */ held = (SFR(PORTB) >> 2) & 1; REG(ANSELB, CLR) = 1u << 2; digital = (SFR(PORTB) >> 2) & 1; /* RB4 with nothing on it, then with its pull-up. */ nopull = (SFR(PORTB) >> 4) & 1; REG(CNPUB, SET) = 1u << 4; pullup = (SFR(PORTB) >> 4) & 1; puts_("RB2 analog="); put_dec(held); puts_(" digital="); put_dec(digital); puts_(" RB4 nopull="); put_dec(nopull); puts_(" pullup="); put_dec(pullup); puts_("\n"); /* IOLOCK: set after the unlock; a write to RPB15R is then ignored. */ before = SFR(RPB15R); unlock(); REG(CFGCON, 0) = SFR(CFGCON) | (1u << 13); SFR(SYSKEY) = 0; SFR(RPB15R) = 0; after = SFR(RPB15R); lock = (SFR(CFGCON) >> 13) & 1; unlock(); SFR(CFGCON) = SFR(CFGCON) & ~(1u << 13); SFR(SYSKEY) = 0; unlocked = !((SFR(CFGCON) >> 13) & 1); puts_("IOLOCK="); put_dec(lock); puts_(" RPB15R kept="); put_dec(before == 5 && after == 5); puts_(" cleared="); put_dec(unlocked); puts_("\nONCE DONE\n"); } int main(void) { uint32_t next; int moved = 0; /* UART1 on RPB3; 115200 at PBCLK 40 MHz with BRGH: 40e6 / (4 * 115200) - 1 = 86. */ REG(ANSELB, CLR) = (1u << 3) | (1u << 13) | (1u << 14) | (1u << 15); SFR(RPB3R) = 1; /* U1TX (group 1, 0001) */ SFR(U1BRG) = 86; SFR(U1STA) = (1u << 10) | (1u << 12); /* UTXEN, URXEN */ SFR(U1MODE) = (1u << 15) | (1u << 3); /* ON, BRGH, 8N1 */ puts_("PIC32MX1/2 bare-metal: start\n"); puts_("DEVID="); put_hex(SFR(DEVID)); puts_(" OSCCON="); put_hex(SFR(OSCCON)); puts_(" RPB3R="); put_dec(SFR(RPB3R)); puts_("\n"); REG(TRISB, CLR) = (1u << 13) | (1u << 14) | (1u << 8); /* OC1: PWM (OCM 110) on Timer3 (OCTSEL), 1 kHz: PBCLK 40 MHz / 8 / 5000, OC1RS 1250. */ SFR(RPB7R) = 5; /* OC1 (group 1, 0101) */ SFR(T3CON) = 0; SFR(TMR3) = 0; SFR(PR3) = 4999; SFR(OC1RS) = 1250; SFR(OC1R) = 1250; SFR(OC1CON) = (1u << 15) | (1u << 3) | 6; SFR(T3CON) = (1u << 15) | (3u << 4); /* ON, 1:8 */ /* Multi-vector mode; the core timer at priority 7 (IPC0<4:2>), Timer2 (vector 8) at 3. */ REG(INTCON, SET) = 1u << 12; SFR(IPC0) = 7u << 2; SFR(IPC2) = 3u << 2; ct_next = mfc0_count() + 20000; mtc0_compare(ct_next); SFR(T2CON) = 0; SFR(TMR2) = 0; SFR(PR2) = 19999; SFR(T2CON) = (1u << 15) | (3u << 4); /* ON, 1:8 */ REG(IFS0, CLR) = (1u << 0) | (1u << 9); REG(IEC0, SET) = (1u << 0) | (1u << 9); __asm__ volatile("ei; ehb"); next = mfc0_count(); for (;;) { next += 10000; while ((int32_t)(mfc0_count() - next) < 0) { } REG(LATB, INV) = 1u << 13; /* RB13 */ if (ct_ticks >= 100 && !moved) { /* OC1 moves from RPB7 to RPB15 (both group 1): RB7 is a port pin again. */ SFR(RPB7R) = 0; SFR(RPB15R) = 5; moved = 1; } if (ct_ticks == 200) { puts_("CT ticks=200 T2 ticks="); put_dec(t2_ticks); puts_(" OC1 on RPB15R="); put_dec(SFR(RPB15R)); puts_("\nDONE\n"); ct_ticks++; /* say it once */ once(); } } }