arm裸机点亮led volatile的理解

本文详细介绍了嵌入式开发中初始化流程、volatile关键字在C语言中的应用,包括同步、防止编译器优化以及直接写入IO端口的重要性。此外,还展示了如何通过C语言控制LED灯,以及使用链接文件优化程序编译过程。
摘要由CSDN通过智能技术生成

一、初始化流程

初始化时钟

设置GPIO的复用IO口

设置GPIO的电气属性

初始化GPIO

二、c语言初始化寄存器volatile理解

#define CCM_CCGR0 *((volatile unsigned int *)0x020C4068)

对 volatile的理解

其中volatile关键字有以下用途:

(1)用来同步,因为同一个东西可能在不同的存储介质中有多个副本,有些情

况下会使得这些副本中的值不同,这是不允许的,所以干脆用volatile,让它只

有一个,没有其他的副本,这样就不会发生不同步的问题。

(2)防止编译器优化去掉某些语句,像我在arm中见到个寄存器非常奇怪,

当中断来的时候,相对应的位置1,而清0又不能向这位写0,向这位写1才是1才

是清中断(清0)

// 假设0x560012300 为寄存器地址
#define INTPAND *(volatile unsigned int *)0x560012300

INTPAND = INTPAND; // 清中断

像编译器如果看到有INTPAND = INTPAND;这种看似无用的操作,如果没有

volatile说明,编译器就很有可能会去掉INTPAND = INTPAND;实际上有用的东

西,却被编译器当没用的东西优化掉了。

(3)当地址是io端口的时候,读写这个地址是不能对它进行缓存的,这是相对

于某些嵌入式中有cache(缓存)才有这个。比如写这个io端口的时候,如果没有这个

volatile,很可能由于编译器的优化,会先把值先写到一个缓冲区,到一定时候

再写到io端口,这样就不能使数据及时的写到io端口,有了volatile说明以后,

就不会再经过cache,write buffer这种,而是直接写到io端口,从而避免了读写

io端口的延时。Cache 是什么? - 知乎 (zhihu.com)

三、c语言版led流程

1.编写启动文件start.s

.global _start
 
 _start:
      @ 设置处理器进入SVC模式
      mrs r0, cpsr //读取cpsr到r0
      bic r0, r0, #0x1f //BIC Rd, Rn, #immed Rd = Rn & (~#immed) 0x1f 1 1111
      orr r0, r0, #0x13 //使用SVC模式 ORR Rd, Rn, #immed Rd = Rn | #immed 0x13 1 0011
      msr cpsr, r0      //将r0写入到cpsr

     @ 设置sp指针
     ldr sp, = 0x80200000
     b main //跳转到c语言到main函数

2.编写main.h文件

#ifndef _MAIN_H
#define _MAIN_H

// 所有时钟寄存器地址
#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)


// 设置复用IO口地址
#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0x020E0068)
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0x020E02F4)

// GPIO相关寄存器地址
#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 *)0x0209C00C)
#define GPIO1_IMR       *((volatile unsigned int *)0x0209C00C)
#define GPIO1_ISR       *((volatile unsigned int *)0x0209C00C)
#define GPIO1_EDGE_SEL  *((volatile unsigned int *)0x0209C00C)


#endif // !_MAIN_H

3.编写main.c文件

#include "main.h"

// 使能外设时钟
void clk_enable(void)
{
    CCM_CCGR0 = 0xFFFFFFFF;
    CCM_CCGR1 = 0xFFFFFFFF;
    CCM_CCGR2 = 0xFFFFFFFF;
    CCM_CCGR3 = 0xFFFFFFFF;
    CCM_CCGR4 = 0xFFFFFFFF;
    CCM_CCGR5 = 0xFFFFFFFF;
    CCM_CCGR6 = 0xFFFFFFFF;
}

// 初始化LED
void led_init(void)
{
    // 复用IO口
    SW_MUX_GPIO1_IO03 = 0x5;
    // 设置IO口的电气属性
    SW_PAD_GPIO1_IO03 = 0x10B0;


    // GPIO初始化
    GPIO1_GDIR = 0x08; //设置为输出
    GPIO1_DR = 0x0;    //设置为打开灯
    
}
// 短延时
void delay_short(volatile unsigned int n)
{
    while(n--){}
}

// 一次大概是1ms 主频396MHZ
// n代表延时的时间
void delay(volatile unsigned int n)
{
    while(n--)
    {
        delay_short(0x7FF);
    }
}

// 打开led
void led_on(void)
{
    GPIO1_DR &= ~(1<<3); // bit3清0  1000  反  0111
}
// 关闭led
void led_off(void)
{
    GPIO1_DR |= (1<<3); // bit3清0  1000  反  0111
}

int main(void)
{
    // 使能时钟
    clk_enable();
    // 初始化led
    led_init();

    while (1)
    {
        led_on();
        delay(500);

        led_off();
        delay(500);
    }
    return 0;
    // 初始化led
    // 闪烁led灯
}

4.编写makefile文件

makefile 文件

# 依赖文件放在前面,链接编译后在前,start.o放在main.o 前
# 可以在反汇编.s文件中查看编译后的执行顺序
objs = start.o main.o 


led.bin : $(objs)
	arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o led.elf
	arm-linux-gnueabihf-objcopy -O binary -S led.elf $@
	arm-linux-gnueabihf-objdump -D -m arm led.elf > led.dis

# -Wall表示显示编译时的所有警告
# nostdlib表示不链接系统标准启动文件和库文件
# -O表示优化等级

%.o : %.c
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

%.o : %.S
	arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

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


 以上是没有用到链接文件的

arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o led.elf

 -Ttext 0x87800000 指定链接首地址

使用链接文件如下

SECTIONS{
	. = 0X87800000;
	.text :
	{
		start.o 
		main.o 
		*(.text)
	}
    .rodata ALIGN(4) : {*(.rodata*)}  
    .data ALIGN(4)   : { *(.data) }
	__bss_start = .;
	.bss ALIGN(4)  : { *(.bss)  *(COMMON) }    
	__bss_end = .;
}

 编译部分改为用链接文件

	arm-linux-gnueabihf-ld -Timx6u.lds $^ -o led.elf
var code = "c01da795-9bfb-4168-89ef-6424a65cb617"

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QStudy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值