Tengo una función que enciende un LED configurando PB1 en ALTO. La luz del LED es apenas visible. Si configuro PB1 en ALTO en mi función principal, la luz del LED es tan brillante como debería ser.
No tiene sentido para mí, ya que solo está cambiando un valor, que es 0 o 1. Debo estar omitiendo algo extremadamente obvio, pero ¿qué podría ser?
Aquí hay alguna información de fondo:
Editar: se agregó más información y etiquetas
Aquí está el código con el pin establecido en main:
#include <avr/io.h>
int main(void) {
DDRB |= (1 << PB1);
PORTB |= (1 << PB1);
while(1) {
}
return 0;
}
Y aquí está el código donde se establece el pin en una función:
#include <avr/io.h>
void turn_on_led();
int main(void) {
DDRB |= (1 << PB1);
turn_on_led();
while(1) {
}
return 0;
}
void turn_on_led()
{
PORTB |= (1 << PB1);
}
Aquí está el archivo MAKE:
main:
avr-gcc -g -Os -Wall -mmcu=atmega328 -c ../src/example.c
elf:
avr-gcc example.o -o example.elf
hex:
avr-objcopy -O ihex example.elf example.hex
dump:
avr-objdump -h -S example.o > example.lst
upload:
avrdude -p m328 -c avrispmkII -P usb -U flash:w:example.hex
clean:
rm -f *.o
rm -f *.hex
rm -f *.lst
Configuración de fusibles:
avrdude: Device signature = 0x1e9514
avrdude: safemode: lfuse reads as E2
avrdude: safemode: hfuse reads as D9
avrdude: safemode: efuse reads as 7
Entrada de configuración de fusibles en una calculadora de fusibles para mostrar bits individuales:
Desmontaje del pasador de ajuste en la tubería principal:
example.o: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000006 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 0000003a 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 0000003a 2**0
ALLOC
3 .debug_abbrev 0000004e 00000000 00000000 0000003a 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_info 00000082 00000000 00000000 00000088 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
5 .debug_line 000000c2 00000000 00000000 0000010a 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
6 .debug_frame 00000020 00000000 00000000 000001cc 2**2
CONTENTS, RELOC, READONLY, DEBUGGING
7 .debug_pubnames 0000001b 00000000 00000000 000001ec 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
8 .debug_pubtypes 0000001e 00000000 00000000 00000207 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
9 .debug_aranges 00000020 00000000 00000000 00000225 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
10 .debug_str 000000d9 00000000 00000000 00000245 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <main>:
#include <avr/io.h>
int main(void) {
DDRB |= (1 << PB0);
0: 20 9a sbi 0x04, 0 ; 4
PORTB |= (1 << PB0);
2: 28 9a sbi 0x05, 0 ; 5
4: 00 c0 rjmp .+0 ; 0x6 <__zero_reg__+0x5>
Desmontaje del pasador de ajuste en función:
example.o: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000000c 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000040 2**0
ALLOC
3 .debug_abbrev 00000061 00000000 00000000 00000040 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_info 00000096 00000000 00000000 000000a1 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
5 .debug_line 000000dc 00000000 00000000 00000137 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
6 .debug_frame 00000030 00000000 00000000 00000214 2**2
CONTENTS, RELOC, READONLY, DEBUGGING
7 .debug_pubnames 0000002b 00000000 00000000 00000244 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
8 .debug_pubtypes 0000001e 00000000 00000000 0000026f 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
9 .debug_aranges 00000020 00000000 00000000 0000028d 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
10 .debug_str 000000e5 00000000 00000000 000002ad 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <turn_on_led>:
return 0;
}
void turn_on_led()
{
PORTB |= (1 << PB1);
0: 29 9a sbi 0x05, 1 ; 5
}
2: 08 95 ret
00000004 <main>:
#include <avr/io.h>
void turn_on_led();
int main(void) {
DDRB |= (1 << PB1);
4: 21 9a sbi 0x04, 1 ; 4
turn_on_led();
6: 0e 94 00 00 call 0 ; 0x0 <turn_on_led>
a: 00 c0 rjmp .+0 ; 0xc <main+0x8>
Vaya, eso es bastante loco. Esos programas son casi idénticos. Solo para facilitar la comparación, el primer programa, con la asignación en main
, tiene el ensamblado (con comentarios):
0: 20 9a sbi 0x04, 0 ; Set Bit of IO for DDRB
2: 28 9a sbi 0x05, 0 ; Set Bit of IO for PORTB
4: 00 c0 rjmp .+0 ; Relative JuMP of 0 bytes functions as `while(1);`
El segundo programa, con la asignación en una función, tiene el ensamblado:
0: 28 9a sbi 0x05, 0 ; Set Bit of IO for PORTB
2: 08 95 ret ; Return to the address on the call stack
4: 20 9a sbi 0x04, 0 ; Set Bit of IO for DDRB
6: 0e 94 00 00 call 0 ; Call the function at address 0 (at top)
a: 00 c0 rjmp .+0 ; Relative JuMP of 0 bytes functions as `while(1);`
El tercer programa realiza una optimización interesante: elimina completamente la llamada a la función, la alinea y hace que este programa sea idéntico al primero. Probablemente podría obtener un efecto idéntico con inline void turn_on_led()
. En aras de la integridad, el conjunto es:
0: 20 9a sbi 0x04, 0 ; Set Bit of IO for DDRB
2: 28 9a sbi 0x05, 0 ; Set Bit of IO for PORTB
4: 00 c0 rjmp .+0 ; Relative JuMP of 0 bytes functions as `while(1);`
Las direcciones 0
a a
son direcciones dentro de la .text
sección, no en la memoria del programa. La sección .text comienza en el desplazamiento 0x34, según la File off[set]
directiva:
Idx Name Size VMA LMA File off Algn
0 .text 00000006 00000000 00000000 00000034 2**0
Lo que realmente está en las direcciones 0-34 es (generalmente) la tabla de vectores de interrupción. Si hubiera seleccionado la BOOTRST
bandera, estaría al comienzo del cargador de arranque, pero no lo hizo, así que no lo está. El primer elemento en la tabla de vectores de interrupción le dice al procesador a dónde ir para reiniciar o arrancar.
Esta ubicación debería ser la primera instrucción de main
estos programas ( 0
para el primer caso, 4
para el segundo caso), pero es posible que tenga una dirección predeterminada 0
en el segundo programa, lo que establecería la salida alta mientras el registro de dirección de datos aún estaba configurado. para ingresar según los valores predeterminados. Esto activaría el pullup débil y el cambio del registro de dirección de datos más adelante no tendría ningún efecto.
Supongo que esto es lo que está pasando. Para probar si su problema es que su salida solo usa el pullup débil, puede eliminar la asignación DDRB por completo de cualquiera de los programas. El resultado debería ser un LED tenuemente iluminado. Si no es el mismo brillo, entonces este no es tu problema. Si es el mismo brillo, supongo que este es, de hecho, su problema.
Otro contratiempo podría ser la mención en la sección 11.2 de la hoja de datos , "Puertos como E/S digital general" que:
Como se muestra en la Figura 11-2, el bit de registro PINxn y el pestillo anterior constituyen un sincronizador. Esto es necesario para evitar la metaestabilidad si el pin físico cambia de valor cerca del borde del reloj interno, pero también introduce un retraso. La Figura 11-3 muestra un diagrama de tiempo de la sincronización cuando se lee un valor de pin aplicado externamente.
Por lo tanto, en cada ejemplo de lectura de un pin, hay una operación nula durante la cual se tiene en cuenta este retraso del sincronizador. Use __no_operation();
en C para este efecto. Esta necesidad de retraso es muy común en la programación de sistemas integrados; es más barato hacer que el programador ponga un retraso en su código que hacer que algunas cosas sucedan en un solo ciclo de reloj.
En su primer programa, no tiene ese retraso. En su segundo programa, tiene un retraso. Esto debería causar que el primer programa no funcione, pero el segundo programa debería funcionar. Esto no es lo que está sucediendo, y no hay tal retraso en las salidas, por lo que dudo que este sea el problema.
Su ensamblaje demuestra que este no es el caso, pero un error común es olvidar el archivo while(1)
. Esto puede hacer que el procesador se reinicie inmediatamente después de encender el LED. Mientras el procesador se reinicia y se configura el registro DDRB, el LED está apagado. Luego, se enciende brevemente y el reinicio comienza de nuevo. Esto forma un sistema PWM rudimentario por accidente , lo que hace que el LED parezca tenue.
Sin embargo, tiene un while(1)
(tenga en cuenta que for(;;)
es un equivalente popular) y aparece en el ensamblaje como rjmp .+0
, por lo que este no parece ser su problema. Estoy un poco confundido por 0
, rjmp
cambia el contador del programa a PC + k + 1. Por lo general, usamos etiquetas para esto cuando escribimos en ensamblador y, por lo tanto, debería generar un resultado k
de -1, pero parece razonable confiar en que el compilador está haciendo lo correcto aquí.
Sin embargo, echemos un mejor vistazo a la codificación. El código hexadecimal de la instrucción es 00 c0
. De acuerdo con el manual del conjunto de instrucciones AVR , el código de operación para rjmp es 1100 kkkk kkkk kkkk
o, en hexadecimal, 0xCK KK, donde la concatenación de K
es k, nuestro salto relativo. El AVR que estamos usando es little-endian, por lo que, 00 C0
como se ve en el programa, es un salto relativo (C) a una posición a 0 bytes de distancia.
Según la descripción de la operación, esta realizará la operación PC <- PC + 0 + 1, o avanzará el contador del programa más allá de esta dirección. Sin embargo, puede ser que esta no sea la interpretación correcta de esta operación, siempre he usado etiquetas al trabajar con esta instrucción, por lo que el ensamblador ha abstraído el número real utilizado.
Acusar al compilador de malinterpretar las líneas
while(1) {
}
es bastante extremo, sin embargo. No creo que este sea el problema.
¡Espero que esto ayude!
Después de leer la información que Kevin proporcionó en su respuesta, leí un documento llamado "AVR32006: Primeros pasos con GCC para AVR32". En este documento había una sección en particular que me hizo pensar en lo que dijo Kevin sobre la tabla de vectores.
El proceso de vinculación necesita información sobre el código y la ubicación de la memoria de datos. El uso de un script de enlace proporciona esto. Si especifica para qué dispositivo está compilando el código mediante el uso de ?-mpart=? opción en avr32-gcc, se utilizará un script de enlace predeterminado para ese dispositivo.
Así que intenté agregar la mmcu
bandera al comando de vinculación, para que se cambiara de:
elf:
avr-gcc example.o -o example.elf
A:
elf:
avr-gcc -mmcu=atmega328 example.o -o example.elf
Este resultó ser el problema, la tabla de vectores no estaba configurada correctamente y faltaban muchas otras partes.
Aquí está el desmontaje después de agregar la mmcu
bandera:
example.elf: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000090 00000000 00000000 00000054 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .stab 000006cc 00000000 00000000 000000e4 2**2
CONTENTS, READONLY, DEBUGGING
2 .stabstr 00000081 00000000 00000000 000007b0 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
74: 0e 94 42 00 call 0x84 ; 0x84 <main>
78: 0c 94 46 00 jmp 0x8c ; 0x8c <_exit>
0000007c <__bad_interrupt>:
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
00000080 <turn_on_pb>:
return 0;
}
void turn_on_pb(void)
{
PORTB |= (1 << PB0);
80: 28 9a sbi 0x05, 0 ; 5
}
82: 08 95 ret
00000084 <main>:
void turn_on_pb(void);
int main(void)
{
DDRB |= (1 << PB0);
84: 20 9a sbi 0x04, 0 ; 4
turn_on_pb();
86: 0e 94 40 00 call 0x80 ; 0x80 <turn_on_pb>
8a: ff cf rjmp .-2 ; 0x8a <main+0x6>
0000008c <_exit>:
8c: f8 94 cli
0000008e <__stop_program>:
8e: ff cf rjmp .-2 ; 0x8e <__stop_program>
Me imagino que su problema es que la pila no está configurada correctamente, por lo que la call
instrucción falla cuando intenta guardar la dirección de retorno. Esto probablemente da como resultado un reinicio del procesador, lo que resulta en un PWM accidental como lo describe Kevin, excepto que es DDRB el que se ve afectado por el PWM y PORTB nunca se configura en absoluto.
clabacchio
#define
s para cambiar el nombre de las salidas de LED y evitar 'números mágicos', ya que da como resultado un código más limpio y legible.rzetterberg
toby jaffey
rzetterberg
escape
vicatcu
rzetterberg
rzetterberg