C语言下的Led灯
阅读原文时间:2023年07月10日阅读:1

1. 设计思想

1.1 设置处理器模式

设置sp啥的汇编要先进入SVC模式,超级管理员特权模式,这样就可以访问所有寄存器了,需要用到cpsr寄存器

0到4位要设置svc模式10011 = 0x13, 就不能用ldr和str了因为不能操作这两个寄存器

1.2 设置sp指针

进入C语言之前要设置出栈和入栈。sp可以指向内部ram,也可以指向ddr。在6u启动的时候上一章讲的,其实ddr已经被初始化了,sp设置到哪里呢?要设置堆栈大小,0x200000 = 2M(随便设置)。

处理器栈增长方式:

对于A7而言是向下增长的,就是从高地址向低地址增长,512M DDR的地址是0x80000000-0x9FFFFFFF, 如果设置成8000000了就相当于0了!那样的话sp就是0x80200000

1.3 跳转到C语言

用b来跳转main函数

2.代码

start.s

.global _start

_start:
    /* 操作cpsr寄存器使其进入到超级管理员模式 */
    mrs r0, cpsr
    bic r0, r0, #0x1f @位消除,将后面5位设置为0
    orr r0, r0, #0x13 @按位或,上面5位设置位0之后就可以赋值进入svc啦
    @ mov r0  #0x13 这种赋值语句,看起来还可以,但是前面27位都为0啦,我们只关心后面5位
    msr cpsr, r0

    /* 设置栈指针 */
    ldr sp,=0x80200000

    /* 跳转C */
    b main

main.h

#ifndef __MAIN_C
#define __MAIN_C

/*
* CCM 相关寄存器地址
*/
#define CCM_CCGR0         *((volatile unsigned int *)0X020C4068)
#define CCM_CCGR1         *((volatile unsigned int *)0X020C406C)
#define CCM_CCGR2         *((volatile unsigned int *)0X020C4070)
#define CCM_CCGR3         *((volatile unsigned int *)0X020C4074)
#define CCM_CCGR4         *((volatile unsigned int *)0X020C4078)
#define CCM_CCGR5         *((volatile unsigned int *)0X020C407C)
#define CCM_CCGR6         *((volatile unsigned int *)0X020C4080)

/*
* IOMUX 相关寄存器地址
*/
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)
/*
* GPIO1 相关寄存器地址
*/
#define GPIO1_DR          *((volatile unsigned int *)0X0209C000)
#define GPIO1_GDIR        *((volatile unsigned int *)0X0209C004)
#define GPIO1_PSR         *((volatile unsigned int *)0X0209C008)
#define GPIO1_ICR1        *((volatile unsigned int *)0X0209C00C)
#define GPIO1_ICR2        *((volatile unsigned int *)0X0209C010)
#define GPIO1_IMR         *((volatile unsigned int *)0X0209C014)
#define GPIO1_ISR         *((volatile unsigned int *)0X0209C018)
#define GPIO1_EDGE_SEL    *((volatile unsigned int *)0X0209C01C)

#endif // !__MAIN_C

main.c

#include "main.h"

void clock_enable()
{
    CCM_CCGR0 = 0xFFFFFFFF;
    CCM_CCGR1 = 0xFFFFFFFF;
    CCM_CCGR2 = 0xFFFFFFFF;
    CCM_CCGR3 = 0xFFFFFFFF;
    CCM_CCGR4 = 0xFFFFFFFF;
    CCM_CCGR5 = 0xFFFFFFFF;
    CCM_CCGR6 = 0xFFFFFFFF;
}

void led_init()
{
    SW_MUX_GPIO1_IO03 = 0x5;
    SW_PAD_GPIO1_IO03 = 0x10b0;

    GPIO1_GDIR = 0x8;
    GPIO1_DR = 0xfffffff7;
}

/*
* 将 GPIO1_DR 的 bit3 清零
*/
void led_on(void)
{
    GPIO1_DR &= ~(1<<3);
}

/*
* @description : 关闭 LED 灯
* @param : 无
* @return : 无
*/
void led_off(void)
{
/*
* 将 GPIO1_DR 的 bit3 置 1
*/
    GPIO1_DR |= (1<<3);
}

void delay_short(volatile unsigned int n)
{
    while(n--);
}

void delay(volatile unsigned int n)
{
    while (n--)
    {
        /* code */
        delay_short(0x7ff);
    }
}

void main()
{
    clock_enable();
    led_init();
    while (1)
    {
        /* code */
        led_on();
        delay(500);

        led_off();
        delay(500);
    }
}

Makefile

objs := start.o main.o # 文件定义

# $^表示所有依赖文件的集合
# $@表示所有生成目标文件
ledc.bin : $(objs)

    arm-linux-gnueabihf-ld -Ttext 0x87800000 -o ledc.elf $^
    arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
    arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis

# $< 表示依赖文件的第一个目标,为啥不用$^?
# 因为比如是main.o main.c,usart.c,main对应的第一个文件没错
# $^是所有的该出错啦
%.o : %.c
    arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o : %.S
    arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
%.o : %.s
    arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

clean:
    rm -rf *.o *.elf *.dis *.bin

链接脚本

SECTIONS
{
    . = 0X87800000;
    .text :
    {
       start.o
       *(.text)
    }
    .rodata ALIGN(4) : {*(.rodata*)}
    .data ALIGN(4) : { *(.data) }
    __bss_start = .;
    .bss ALIGN(4) : { *(.bss) *(COMMON) }
    __bss_end = .;
}
  • 其中.是一个定位计时器,默认是0,如果赋值了,就说明程序在这地方定位了。所以是87800000
  • text段,调用C函数之前肯定要先汇编,所以先汇编,然后所有文件的text段都放在text段中
  • 同理rodata(只读)和data段都放在相应的段里,4字节对齐。但是假如我想把它们放在别的地方 ,需要再用 .= 指定一段内存
  • bss段,没有初始化的变量,初始化为0保存在bss段内,没有初始化的在COMMON
  • bss_start和end直接赋值为0,就是没有被初始化的,手动清0,直接把内存赋值为0

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章