¿Cómo cambia la fuente de reloj dinámicamente en un ATtiny1634?

1

Estoy usando un ATtiny1634 para Mi proyecto y yo queríamos asegurarnos de que el cristal externo de 8MHz se soldara correctamente antes de configurar el fusible para seleccionarlo en el arranque. Así que aquí está mi código de lo que encontré en la hoja de datos:

void setup()
{
    // Select external 8MHz crystal
    CCP = 0xD8;                // ATtiny1634 Signature
    CLKSR = 0b1101 | _BV(CSTR);
    loop_until_bit_is_set(CLKSR, OSCRDY);

    // Set prescaler to unity
    CCP = 0xD8;                // ATtiny1634 Signature
    CLKPR = 0;
    loop_until_bit_is_set(CLKSR, OSCRDY);
}

Esta es solo la función de configuración que me interesa aquí. En una función de bucle, alterno el estado de una salida que está conectada a un zumbador (sin un oscilador interno) para poder escuchar el tono resultante. Por supuesto, introduje un poco de retraso para hacer que el sonido sea audible.

Hay 2 cosas que noté. Primero, el prescaler no parece cambiar. Debería escuchar un sonido 8 veces más alto, pero sigue siendo el mismo. A continuación, el microcontrolador a veces parece detenerse, luego escucho un ruido de silbido proveniente del búfer y la frecuencia aumenta lentamente hasta el tono esperado . El tono es estable después de, digamos, 8-10 segundos.

¿Esto es normal? ¿O me he perdido algo?

Por extraño que parezca, el zumbador hace un ruido sordo cuando toco las almohadillas XTAL pins (en las que el cristal está soldado) con mi sonda de alcance. Eso sugiere que el cristal realmente está funcionando ... ¿verdad? Ese [aumento lento del reloj] no ocurre si comento la selección del reloj. Y no importa si utilizo la configuración de la sonda de rango 10x o no. También utilicé dos 15pF con el cristal, como se indica en la hoja de datos (entre 12 y 22pF).

EDIT : Resulta que el primer problema fue un PEBCAK. Para una prueba, compilé el programa y configuré F_CPU a 1MHz en la línea de comandos (con el valor predeterminado de preescala, también conocido como 8 ). Para la segunda prueba, preescala programada en 1, había usado el valor F_CPU predeterminado de mi archivo make, que es 8MHz . Por lo tanto, es absolutamente normal que no haya una diferencia en el tono generado.

Acabo de olvidar que el problema restante es el del cristal externo. Todavía no puedo descartar una unión de soldadura defectuosa, por eso quise seleccionar el reloj dinámicamente.

EDIT : a continuación se muestra una vista del cristal y las pistas.

Coloqué el cristal y las tapas lo más cerca posible del MCU. Ya he usado este diseño para un ATmega64M1 sin ningún problema.

EDIT : A continuación se encuentra el código de ensamblaje (para fines de prueba), ahora con los nombres de registro adecuados.

main:     format de fichier elf32-avr


Déassemblage de la section .text :

00000000 <__vectors>:
   0:   37 c0           rjmp    .+110       ; 0x70 <__ctors_end>
   4:   49 c0           rjmp    .+146       ; 0x98 <__bad_interrupt>
   8:   47 c0           rjmp    .+142       ; 0x98 <__bad_interrupt>
   c:   45 c0           rjmp    .+138       ; 0x98 <__bad_interrupt>
  10:   43 c0           rjmp    .+134       ; 0x98 <__bad_interrupt>
  14:   41 c0           rjmp    .+130       ; 0x98 <__bad_interrupt>
  18:   3f c0           rjmp    .+126       ; 0x98 <__bad_interrupt>
  1c:   3d c0           rjmp    .+122       ; 0x98 <__bad_interrupt>
  20:   3b c0           rjmp    .+118       ; 0x98 <__bad_interrupt>
  24:   39 c0           rjmp    .+114       ; 0x98 <__bad_interrupt>
  28:   37 c0           rjmp    .+110       ; 0x98 <__bad_interrupt>
  2c:   35 c0           rjmp    .+106       ; 0x98 <__bad_interrupt>
  30:   33 c0           rjmp    .+102       ; 0x98 <__bad_interrupt>
  34:   31 c0           rjmp    .+98        ; 0x98 <__bad_interrupt>
  38:   2f c0           rjmp    .+94        ; 0x98 <__bad_interrupt>
  3c:   2d c0           rjmp    .+90        ; 0x98 <__bad_interrupt>
  40:   2b c0           rjmp    .+86        ; 0x98 <__bad_interrupt>
  44:   29 c0           rjmp    .+82        ; 0x98 <__bad_interrupt>
  48:   27 c0           rjmp    .+78        ; 0x98 <__bad_interrupt>
  4c:   25 c0           rjmp    .+74        ; 0x98 <__bad_interrupt>
  50:   23 c0           rjmp    .+70        ; 0x98 <__bad_interrupt>
  54:   21 c0           rjmp    .+66        ; 0x98 <__bad_interrupt>
  58:   1f c0           rjmp    .+62        ; 0x98 <__bad_interrupt>
  5c:   1d c0           rjmp    .+58        ; 0x98 <__bad_interrupt>
  60:   1b c0           rjmp    .+54        ; 0x98 <__bad_interrupt>
  64:   19 c0           rjmp    .+50        ; 0x98 <__bad_interrupt>
  68:   17 c0           rjmp    .+46        ; 0x98 <__bad_interrupt>
  6c:   15 c0           rjmp    .+42        ; 0x98 <__bad_interrupt>
    ...

00000070 <__ctors_end>:
  70:   11 24           eor r1, r1
  72:   1f be           out SREG, r1    ; 63
  74:   cf ef           ldi r28, 0xFF   ; 255
  76:   d4 e0           ldi r29, 0x04   ; 4
  78:   de bf           out SPH, r29    ; 62
  7a:   cd bf           out SPL, r28    ; 61

0000007c <setup>:
#define SELECT  C,2
#define BUZZER  A,6

void setup()
{
    // Wait for clock to be stable (probably useless here but...)
  7c:   02 b6           in  r0, CLKSR   ; 50
  7e:   07 fe           sbrs    r0, 7
  80:   fd cf           rjmp    .-6         ; 0x7c <setup>
    CCP = CCPSIG;
    CLKSR = 0b1101 | _BV(CSTR); // Select 8MHz external crystal
    loop_until_bit_is_set(CLKSR, OSCRDY);
#endif

    // Change clock prescaler to unity
  82:   88 ed           ldi r24, 0xD8   ; 216
  84:   8f bd           out CCP, r24    ; 47
    clock_prescale_set(clock_div_1);
  86:   13 be           out CLKPR, r1   ; 51
    //~ CCP = CCPSIG;
  88:   02 b6           in  r0, CLKSR   ; 50
  8a:   07 fe           sbrs    r0, 7
  8c:   fd cf           rjmp    .-6         ; 0x88 <setup+0xc>
    //~ CLKPR = 0;
    loop_until_bit_is_set(CLKSR, OSCRDY);

  8e:   42 9a           sbi DDRC, 2 ; 8
    // Setup pins
  90:   86 9a           sbi DDRA, 6 ; 16
    set_output(SELECT);
  92:   4a 9a           sbi PORTC, 2    ; 9
    set_output(BUZZER);
  94:   0b d0           rcall   .+22        ; 0xac <main>
  96:   0c c0           rjmp    .+24        ; 0xb0 <_exit>

00000098 <__bad_interrupt>:
  98:   b3 cf           rjmp    .-154       ; 0x0 <__vectors>

0000009a <loop>:
    set_pin(SELECT);
}

void loop()
  9a:   91 b3           in  r25, PORTA  ; 17
  9c:   80 e4           ldi r24, 0x40   ; 64
  9e:   89 27           eor r24, r25
  a0:   81 bb           out PORTA, r24  ; 17
    milliseconds can be achieved.
 */
void
_delay_loop_2(uint16_t __count)
{
    __asm__ volatile (
  a2:   84 ef           ldi r24, 0xF4   ; 244
  a4:   91 e0           ldi r25, 0x01   ; 1
  a6:   01 97           sbiw    r24, 0x01   ; 1
  a8:   f1 f7           brne    .-4         ; 0xa6 <loop+0xc>
  aa:   08 95           ret

000000ac <main>:
{
    toggle_pin(BUZZER);
    _delay_us(250);
}

  ac:   f6 df           rcall   .-20        ; 0x9a <loop>
  ae:   fe cf           rjmp    .-4         ; 0xac <main>

000000b0 <_exit>:
  b0:   f8 94           cli

000000b2 <__stop_program>:
  b2:   ff cf           rjmp    .-2         ; 0xb2 <__stop_program>
    
pregunta

1 respuesta

2

Confirmo que esto es, de hecho, cómo cambiar el reloj de MCU programáticamente, por lo que no hay nada de malo en el código. El problema sobre el comienzo lento fue, como sospechaba, una unión de soldadura defectuosa debajo de las almohadillas de cristal.

Con una pistola de calor, calenté el tablero (con el cristal) a 180 ° C-220 ° C (después de una etapa de precalentamiento de alrededor de 150 ° C), realicé la unión de soldadura con soldadura fresca y flujo suficiente. y ... voilà ! el problema " inicio lento " desapareció y ahora puedo ver el seno de 8 MHz en ambos pines del ATtiny1634 [ EDIT : sin alterar la señal, esta vez].

    
respondido por el user59864

Lea otras preguntas en las etiquetas