Немного отвлечённо, но общие соображения: 1) нельзя из C переключить OSCCON (ибо код получается разный с/без оптимизации и нет гарантии работы). 2) надо *всегда* программировать все FSR, особенно касающиеся конфигурации ног, с нуля как положено (в т.ч. и АЦП и т.п.) Я это делаю так всё:
/* FUSE bits */
#ifdef __DEBUG
#define CONFIG1 (GCP_OFF & BKBUG_OFF)
#else
#define CONFIG1 (GCP_ON & BKBUG_ON)
#endif
_CONFIG1(JTAGEN_OFF & CONFIG1 & GWRP_OFF & ICS_PGx2 & FWDTEN_ON
& WINDIS_OFF & FWPSA_PR128 & WDTPS_PS32768);
_CONFIG2(IESO_ON & FNOSC_FRC & FCKSM_CSECME & OSCIOFNC_OFF & IOL1WAY_ON & POSCMOD_XT);
/* unlock OSCCONL, INTERRUPTS MUST BE DISABLED!
* W1, W2, W3 will not be preserved! */
#define OSCCONL_UNLOCK() \
asm("mov #OSCCONL, W1"); \
asm("mov.b #0x46, W2"); \
asm("mov.b #0x57, W3"); \
asm("mov.b W2, [W1]"); \
asm("mov.b W3, [W1]");
/* unlock OSCONH, write desired clocking mode, then
* unlock OSCCONL and switch frequency to desired mode,
* INTERRUPTS MUST BE DISABLED! */
static void osccon_switch_clock(unsigned char mode)
{
/* unlock OSCCONH */
asm("mov #OSCCONH, W1");
asm("mov.b #0x78, W2");
asm("mov.b #0x9a, W3");
asm("mov.b W2, [W1]");
asm("mov.b W3, [W1]");
/* set new mode */
OSCCONH=mode;
/* switch clock to new mode */
OSCCONL_UNLOCK();
OSCCONbits.OSWEN=1;
}
/* switch CPU clock to desired mode, no wait completion */
static void cpu_clock_switch(char mode)
{
int cpu_ipl;
SET_AND_SAVE_CPU_IPL(cpu_ipl, 7);
/* if PLL used -- at first switch to FRC without PLL! */
switch ((OSCCON>>12) & 7) {
case 0: /* FRC */
case 2: /* Primary without PLL */
case 4: /* SOSC */
case 5: /* LPRC */
case 7: /* FRCDIV */
break;
default:
/* turn off PLL */
osccon_switch_clock(0); /* select FRC */
/* wait until switch complete */
while (OSCCONbits.OSWEN);
}
/* switch to desired mode (without PLL! see above) */
osccon_switch_clock(mode);
RESTORE_CPU_IPL(cpu_ipl);
}
/* wait few milliseconds until quartz oscillator and PLL will be ready,
* when check OSWEN and return non-zero error code if OSWEN is set */
static int check_xt_osc(void)
{
unsigned n;
/* at highest speed we have ~16000 cycles per millisecond */
n=32767; do {
asm("nop");
} while (--n);
if (! OSCCONbits.OSWEN) return 0;
if (! OSCCONbits.LOCK) return 2; // TODO define error codes
else return 1;
}
/* set default/safe GPIO ports states */
static void gpio_set_safe(void)
{
/* disable pull-ups and pull-downs on ALL pins */
CNPU1=CNPU2=CNPU3=CNPU4=CNPU5=CNPU6=0;
CNPD1=CNPD2=CNPD3=CNPD4=CNPD5=CNPD6=0;
/* configure unused ADC pins as digital -- enabled AN1,3,4,9,10 */
AD1PCFGL=0xffff & ~(1<<1) & ~(1<<3) & ~(1<<4) & ~(1<<9) & ~(1<<10);
AD1PCFGH=0xffff;
/* PORTA */
LATA = 0;
TRISA = 1<<15 | 1<<14;
CNPU3bits.CN43PUE=1, CNPU3bits.CN44PUE=1;
/* PORTB */
LATB = 1<<5 | 1<<8;
TRISB = BIN(00100110)<<8 | BIN(11011010);
CNPU2bits.CN31PUE=1;
CNPU2bits.CN24PUE=1, CNPU2bits.CN25PUE=1; /* PGEC/PGED pull-up */
/* PORTC */
LATC = 0;
TRISC = 1<<15 | 1<<14 | 1<<12 | 1<<1;
/* PORTD */
LATD = 1<<15 | 1<<13; /* LATD8==0 !!! */
TRISD = 1<<14 | 1<<12 | 1<<10 | 1<<5 | 1<<2 | 1<<0;
CNPU4bits.CN49PUE=1, CNPU4bits.CN57PUE=1, CNPU2bits.CN20PUE=1;
/* PORTE */
LATE = 0;
TRISE = 1<<9 | 1<<6 | 1<<5 | 1<<4 | 1<<3 | 1<<2;
CNPU5bits.CN67PUE=1;
/* PORTF */
LATF = 1<<7 | 1<<6; /* LATF0=0 ! */
TRISF = 1<<8 | 1<<2;
/* PORTG */
LATG = 0;
TRISG = 1<<7;
}
/* tune remappable peripheral */
static void remap_peripheral(void)
{
int cpu_ipl;
SET_AND_SAVE_CPU_IPL(cpu_ipl, 7); /* disable interrupts */
/* unlock IOLOCK bit */
OSCCONL_UNLOCK();
OSCCONbits.IOLOCK=0;
/* UART1 -- debug interface */
RPINR18bits.U1RXR = 34; /* RPI34 */
RPOR9bits.RP18R = 3; /* U1TX */
/* UART2 -- modem */
RPINR19bits.U2RXR = 43;
RPOR2bits.RP5R = 5; /* U2TX */
/* UART3 -- GPS */
RPINR17bits.U3RXR = 26;
RPOR10bits.RP21R = 28; /* U3TX */
/* SPI */
RPOR11bits.RP22R = 8; /* SCK1OUT */
RPOR12bits.RP25R = 7; /* SDO1 */
RPINR20bits.SDI1R = 42;
/* OC1, OC2 -- LEDs */
/* unused now */
/* OC3, OC4 -- sound */
RPOR12bits.RP24R = 20; /* OC3 */
RPOR9bits.RP19R = 21; /* OC4 */
/* disable future remapping */
OSCCONL_UNLOCK();
OSCCONbits.IOLOCK=1;
RESTORE_CPU_IPL(cpu_ipl);
}
/* this function called before C runtime initialization
* to configure hardware as soon as possible.
* XXX: variables will not be initilized whem this function called! */
void __attribute__((user_init)) hardware_init(void)
{
SET_CPU_IPL(7); /* disable interrupts except level 7 */
asm("CLRWDT");
remap_peripheral();
/* XXX NOTE: именно здесь, в этой функции отключаются
* и включаются АЦП, настроиваются ноги на ввод-вывод --
* функция вызывается практически сразу после старта
* микроконтроллера (до инициализации C-рантайма,
* до медленного запуска PLL и т.п.) */
gpio_set_safe();
[...]
/* primary oscillator with PLL -- запускается основной
* генератор и не дожидаясь ничего происходит возврат
* в C-стартап для инициализации C-рантайма */
cpu_clock_switch(0x03);
}
int main()
{
int i;
asm("CLRWDT");
signal(SIGABRT, aborted);
/* В main первым делом проверяется, что генератор и PLL
* работает как положено, иначе предпринимаются меры
* по аварийной индикации... */
i=check_xt_osc();
if (i) _Exit(ERR_OSC_FAIL);
[...]
}
Запуск функции hardware_init() обеспечивается модифицированным C-стартапом (там была опция --data-init, но она вызывала функцию после инициализации C-рантайма, а не до -- что неадекватно, потому, что на внутреннем генераторе оно запускается долго и за это время все релюшки поклацают и т.п., а они в hardware_init() выключаются):
;; C Run-time startup module for dsPIC30 C compiler.
;; (c) Copyright 2002,2004,2007 Microchip Technology, All rights reserved
;; 1. initialize stack and stack limit register
;; 2. initialize PSV window if __const_length > 0
;; 3. call the .user_init section, if it exists XXX fk0: call user_init first!
;; 4. process the data initialization template
;; 5. call the user's _main entry point
;; Assigned to section .init, which may be allocated
;; at a specific address in linker scripts.
;; If a local copy of this file is customized, be sure
;; to choose a file name other than crt0.s or crt1.s.
.section .init,code
.global __resetPRI
.ifdef __C30ELF
.type __resetPRI,@function
.endif
__resetPRI:
.weak __reset
.ifdef __C30ELF
.type __reset,@function
.endif
__reset:
;; Initialize stack, PSV, and data
;; registers used: w0
;; Inputs (defined by user or linker):
;; __SP_init
;; __SPLIM_init
;; Outputs:
;; (does not return - resets the processor)
;; Calls:
;; __psv_init
;; __data_init
;; _main
.weak __user_init, __has_user_init
mov #__SP_init,w15 ; initialize w15
mov #__SPLIM_init,w0 ;
mov w0,_SPLIM ; initialize SPLIM
nop ; wait 1 cycle
rcall __psv_init ; initialize PSV
mov #__has_user_init,w0
cp0 w0 ; user init functions?
bra eq,1f ; br if not
call __user_init ; else call them
1:
rcall __data_init ; initialize data
call _main ; call user's main()
.pword 0xDA4000 ; halt the simulator
reset ; reset the processor
.global __psv_init
__psv_init:
;; Initialize PSV window if _constlen > 0
;; Registers used: w0
;; Inputs (defined by linker):
;; __const_length
;; __const_psvpage
;; Outputs:
;; (none)
;;
.equiv PSV, 0x0002
bclr _CORCON,#PSV ; disable PSV (default)
mov #__const_length,w0 ;
cp0 w0 ; test length of constants
bra z,1f ; br if zero
mov #__const_psvpage,w0 ;
mov w0,_PSVPAG ; PSVPAG = psvpage(constants)
bset _CORCON,#PSV ; enable PSV
1: return ; and exit
.global __data_init
__data_init:
;; Process data init template
;; The template is null-terminated, with records
;; in the following format:
;; struct data_record {
;; char *dst; /* destination address */
;; int len; /* length in bytes */
;; int format; /* format code */
;; char dat[0]; /* variable length data */
;; };
;;
;; Registers used: w0 w1 w2 w3 w4 w5
;;
;; Inputs (defined by linker):
;; __dinit_tbloffset
;; __dinit_tblpage
;;
;; Outputs:
;; (none)
;;
;; Calls:
;; __memcpypd3
;;
.equiv FMT_CLEAR,0 ; format codes
.equiv FMT_COPY2,1 ;
.equiv FMT_COPY3,2 ;
mov #__dinit_tbloffset,w0 ; w0,w1 = template
mov #__dinit_tblpage,w1 ;
bra 4f ; br to continue
1: add w0,#2,w0 ; template+=2
addc w1,#0,w1 ;
mov w1,_TBLPAG ; TBLPAG = tblpage(template)
tblrdl.w [w0],w3 ; w3 = len
add w0,#2,w0 ; template+=2
addc w1,#0,w1 ;
mov w1,_TBLPAG ; TBLPAG = tblpage(template)
tblrdl.w [w0],w5 ; w5 = format
add w0,#2,w0 ; template+=2
addc w1,#0,w1 ;
clr w4 ; upper = FALSE (default)
cp w5,#FMT_CLEAR ; test format
bra nz,2f ; br if not FMT_CLEAR
;; FMT_CLEAR - clear destination memory
9: clr.b [w2++] ; clear memory
dec w3,w3 ; done?
bra gtu,9b ; loop if not
bra 4f ; br to continue
;; FMT_COPY2, FMT_COPY3 - copy bytes
2: cp w5,#FMT_COPY2 ; test format
bra z,3f ; br if FMT_COPY2
setm w4 ; upper = TRUE
3: rcall __memcpypd3 ; copy 2 or 3 bytes
4: mov w1,_TBLPAG ; TBLPAG = tblpage(template)
tblrdl.w [w0],w2 ; w2 = next dst
cp0 w2 ;
bra nz,1b ; loop on non-zero dst
retlw #0,w0 ; exit (clears ARGC also)
__memcpypd3:
;;
;; Copy data from program memory to data memory
;;
;; Registers used: w0 w1 w2 w3 w4 w5
;;
;; Inputs:
;; w0,w1 = source address (24 bits)
;; w2 = destination address (16 bits)
;; w3 = number of bytes (even or odd)
;; w4 = upper byte flag (0 = false)
;;
;; Outputs:
;; w0,w1 = next source address (24 bits)
;;
1: mov w1,_TBLPAG ; TBLPAG = tblpage(src)
mov w0,w5 ; w5 = tbloffset(src)
add w0,#2,w0 ; src+=2
addc w1,#0,w1 ;
tblrdl.b [w5++],[w2++] ; dst++ = lo byte
dec w3,w3 ; num--
bra z,2f ; br if done
tblrdl.b [w5--],[w2++] ; dst++ = hi byte
dec w3,w3 ; num--
bra z,2f ; br if done
cp0 w4 ; test upper flag
bra z,1b ; br if false
tblrdh.b [w5],[w2++] ; dst++ = upper byte
dec w3,w3 ; num--
bra nz,1b ; br if not done
2: return ; exit
.end