Arduino C代码不会 Flink LED [重复]

3zwtqj6y  于 4个月前  发布在  Flink
关注(0)|答案(2)|浏览(55)

此问题在此处已有答案

Makefile failing to flash code to ATmega328p(1个答案)
20天前关闭。
我试图在Arduino上学习嵌入式C的裸金属,没有IDE的方式.我尝试写一些avr/io.h宏自己以不同的方式.我尝试 Flink LED通过使用直接内存地址(它的作品)和指针的结构位字段(* 这不工作 *).
定义了F_CPU宏。设备:Arduino UNO R3板,其具有ATMEGA328P芯片。
直接内存扩展(按预期工作):

#include <util/delay.h>

int main (void) {

    *(volatile unsigned char*) 0x24 |= 1 << 5;

    while(1) {

        *(volatile unsigned char*) 0x25 |= 1 << 5;

        _delay_ms(500);

        *(volatile unsigned char*) 0x25 &= ~(1 << 5);

        _delay_ms(500);
    }

    return 0;
}

字符串
通过编译器组装:

.file   "main.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
    .text
    .section    .text.startup,"ax",@progbits
.global main
    .type   main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    sbi 0x4,5
.L2:
    sbi 0x5,5
    ldi r18,lo8(1599999)
    ldi r24,hi8(1599999)
    ldi r25,hlo8(1599999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    cbi 0x5,5
    ldi r18,lo8(1599999)
    ldi r24,hi8(1599999)
    ldi r25,hlo8(1599999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    rjmp .L2
    .size   main, .-main
    .ident  "GCC: (Fedora 13.2.0-1.fc38) 13.2.0"


结构与位字段的方式(不工作,LED刚刚打开):

#include <util/delay.h>

typedef unsigned char   byte;

volatile struct {
    volatile byte PINB0: 1;
    volatile byte PINB1: 1;
    volatile byte PINB2: 1;
    volatile byte PINB3: 1;
    volatile byte PINB4: 1;
    volatile byte PINB5: 1;
    volatile byte PINB6: 1;
    volatile byte PINB7: 1;
} *__ATMEGA_328P_PINB = (void*) 0x23;

volatile struct {
    volatile byte DDB0: 1;
    volatile byte DDB1: 1;
    volatile byte DDB2: 1;
    volatile byte DDB3: 1;
    volatile byte DDB4: 1;
    volatile byte DDB5: 1;
    volatile byte DDB6: 1;
    volatile byte DDB7: 1;
} *__ATMEGA_328P_DDRB = (void*) 0x24;

volatile struct {
    volatile byte PORTB0: 1;
    volatile byte PORTB1: 1;
    volatile byte PORTB2: 1;
    volatile byte PORTB3: 1;
    volatile byte PORTB4: 1;
    volatile byte PORTB5: 1;
    volatile byte PORTB6: 1;
    volatile byte PORTB7: 1;
} *__ATMEGA_328P_PORTB = (void*) 0x25;

int main (void) {

    __ATMEGA_328P_DDRB->DDB5 = 1;

    while(1) {
        __ATMEGA_328P_PORTB->PORTB5 = 1;

        _delay_ms(500);

        __ATMEGA_328P_PORTB->PORTB5 = 0;

        _delay_ms(500);
    }

    return 0;
}


通过编译器组装:

.file   "main.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
    .text
    .section    .text.startup,"ax",@progbits
.global main
    .type   main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    lds r30,__ATMEGA_328P_DDRB
    lds r31,__ATMEGA_328P_DDRB+1
    ld r24,Z
    ori r24,lo8(1<<5)
    st Z,r24
.L2:
    lds r30,__ATMEGA_328P_PORTB
    lds r31,__ATMEGA_328P_PORTB+1
    ld r24,Z
    ori r24,lo8(1<<5)
    st Z,r24
    ldi r18,lo8(1599999)
    ldi r24,hi8(1599999)
    ldi r25,hlo8(1599999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    lds r30,__ATMEGA_328P_PORTB
    lds r31,__ATMEGA_328P_PORTB+1
    ld r24,Z
    andi r24,lo8(~(1<<5))
    st Z,r24
    ldi r18,lo8(1599999)
    ldi r24,hi8(1599999)
    ldi r25,hlo8(1599999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    rjmp .L2
    .size   main, .-main
.global __ATMEGA_328P_PORTB
    .data
    .type   __ATMEGA_328P_PORTB, @object
    .size   __ATMEGA_328P_PORTB, 2
__ATMEGA_328P_PORTB:
    .word   37
.global __ATMEGA_328P_DDRB
    .type   __ATMEGA_328P_DDRB, @object
    .size   __ATMEGA_328P_DDRB, 2
__ATMEGA_328P_DDRB:
    .word   36
.global __ATMEGA_328P_PINB
    .type   __ATMEGA_328P_PINB, @object
    .size   __ATMEGA_328P_PINB, 2
__ATMEGA_328P_PINB:
    .word   35
    .ident  "GCC: (Fedora 13.2.0-1.fc38) 13.2.0"
.global __do_copy_data


我的Makefile:

PROGRAM := main
PORT := 8080
MEMORY := 512M

ARDUINO_DEV := /dev/ttyACM0

# Toolchains
TOOLCHAIN := avr

CC := $(TOOLCHAIN)-gcc
LD := $(TOOLCHAIN)-ld
OBJCPY := $(TOOLCHAIN)-objcopy
FLASH := avrdude

# File Names
C_DIR := src
C_FILES := $(wildcard ./$(C_DIR)/*.c)

OUTPUT_DIR := build
OUTPUT_FILES := $(patsubst ./$(C_DIR)/%.c, ./$(OUTPUT_DIR)/%.c.o, $(C_FILES))

# Flags
# https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html
C_FLAGS := -mmcu=atmega328p \
    -Wall \
    -O2 \
    -save-temps \
    -c \
    -DF_CPU=16000000UL

FLASH_FLAGS := -p m328p \
    -c arduino \
    -e \
    -P $(ARDUINO_DEV)

all: $(OUTPUT_DIR)/$(PROGRAM).hex

clean:
    @rm -f build/**
    @printf "[CLEANED UP]\n"

./$(OUTPUT_DIR)/%.c.o: ./$(C_DIR)/%.c
    @$(CC) $(C_FLAGS) -o $@ $<
    @printf "[COMPILED]\t$@!\n"

./$(OUTPUT_DIR)/%.s.o: ./$(ASM_DIR)/%.s
    @$(CC) $(C_FLAGS) -o $@ $<
    @printf "[COMPILED]\t$@!\n"

$(OUTPUT_DIR)/$(PROGRAM).elf: $(OUTPUT_FILES)
    @$(LD) -o $(OUTPUT_DIR)/$(PROGRAM).elf $(OUTPUT_FILES) -Map=$(OUTPUT_DIR)/linker.map
    @printf "[LINKED]\t$@!\n"

$(OUTPUT_DIR)/$(PROGRAM).hex: $(OUTPUT_DIR)/$(PROGRAM).elf
    @$(OBJCPY) -O ihex $(OUTPUT_DIR)//$(PROGRAM).elf $(OUTPUT_DIR)/$(PROGRAM).hex
    @printf "[CREATE BIN]\t$@!\n"

flash: $(OUTPUT_DIR)/$(PROGRAM).hex
    @sudo $(FLASH) $(FLASH_FLAGS) -U flash:w:$(OUTPUT_DIR)/$(PROGRAM).hex
    @printf "[FLASHED]\t$<!\n"

.PHONY: all clean flash


我的目录结构:

.
├── build
│   ├── linker.map
│   ├── main.c.i
│   ├── main.c.o
│   ├── main.c.s
│   ├── main.elf
│   └── main.hex
├── compile_commands.json
├── Makefile
└── src
    └── main.c

3 directories, 9 files


编辑:添加了完整的程序集(它是编译器由于-save-temps而生成的程序集,而不是objdump的反汇编)和我的Makefile

cig3rfwq

cig3rfwq1#

它看起来像你把指针转换为void*,它把它当作16位指针而不是8位指针。

lds r30,__ATMEGA_328P_PORTB
lds r31,__ATMEGA_328P_PORTB+1

字符串
看一看在__ATMEGA_328P_PORTB的数据表中写了什么,我假设它看起来是这样的

__ATMEGA_328P_PINB:
    .word   35
__ATMEGA_328P_DDRB:
    .word   36
__ATMEGA_328P_PORTB:
    .word   37


因此,它实际上会将0x2425加载到Z寄存器中,而不是预期的0x24

6qfn3psc

6qfn3psc2#

问题似乎是,无论你使用的是什么编译器,它都不理解你试图将地址加载到寄存器对中,它试图这样做:

lds r30,__ATMEGA_328P_DDRB
lds r31,__ATMEGA_328P_DDRB+1

字符串
这里实际发生的事情相当于:

r30 = content of address 0x24 (state of DDRB)
r31 = content of address 0x25 (state of PORTB)


或者在更多的C风格语法中:

r30 = *(char*)0x24
r31 = *((char*)0x24+1)


这意味着指针寄存器Z(也称为r30:r31)指向地址0x2524,而不是DDRB寄存器的地址0x0024
如果你想通过指针访问寄存器,你会希望生成的程序集是:

ldi r30, low(__ATMEGA_328P_DDRB)
ldi r31, high(__ATMEGA_328P_DDRB)


为了实现这一点,你可以使用与第一个例子等效的东西:

int main (void) {

    *__ATMEGA_328P_DDRB |= (1 << DDB5);

    while(1) {
        *__ATMEGA_328P_PORTB |= (1 << PB5);

        _delay_ms(500);

        *__ATMEGA_328P_PORTB &= ~(1 << PB5)

        _delay_ms(500);
    }

    return 0;
}


我理解你希望使用位集来获得更好的语法,但是如果没有一些讨厌的预处理器宏和编写纯汇编,这是做不到的。

相关问题