前面刚学习了汇编 led 驱动的编写和验证,现在开始就要进入 C 语言 led 驱动编写与验证了 !
1、C 语言运行环境构建
1.1、设置处理器模式
使 6ULL 处于 SVC 模式下,之前已经提到了处理器的九种模式,参考:【ARM 裸机】汇编 led 驱动之基本语法,如何设置成 SVC 模式,需要用到 CPSR 寄存器,CPSR 寄存器一共是 32 位,关注它的第 0~4 位,也就是设置为 M[4:0] 为 10011 = 0x13;
读写状态寄存器需要用到 MRS 和 MSR 指令,不能使用 LDR 和 STR 指令来对状态寄存器读写了;
1.2、设置 sp 指针
sp 指针可以指向内部 RAM,也可以指向 DDR,这里设置成指向 DDR,sp 设置到哪里?以正点原子开发板为例,512 MB 的范围是 0x80000000~0x9FFFFFFF,假定设置的栈的大小是 2 MB(0x20000),A7 的栈增长方式为向下增长,所以要设置成 sp 指针指向 0x80200000;
1.3、汇编跳转到 C 语言
使用 b 指令跳转到 C 语言的函数,比如 main 函数;
2、驱动编写
在 /home/zsw/linux/IMX6ULL/board_drivers 目录下新建一个 2_ledc 的目录,打开 VScode,在 VScode 中打开文件夹 2_ledc,然后将工作区另存为 ledc,然后新建文件 start.s、main.c、main.h、Makefile 这四个文件;
start.s
.global _start
_start:
// 设置处理器为 SVC 模式
mrs r0, cpsr // 读取 cpsr 到 r0
bic r0, r0, #0x1f // r0 & ~0x1f 清除 cpsr 的 bit4~0 bic 位清除指令
orr r0, r0, #0x13 // r0 | 0x13 设置成 SVC 模式 orr 按位或
msr cpsr, r0 // 把 r0 写入 cpsr
// 设置 sp 指针
ldr sp, =0x80200000
// 跳转到 c 语言
b main
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)
{
SW_MUX_GPIO1_IO03 = 0x5; // 复用为 GPIO1_IO03
SW_PAD_GPIO1_IO03 = 0x10b0; // 配置电气属性
// GPIO 初始化
GPIO1_GDIR = 0x8; // 设置为输出
GPIO1_DR = 0x0; // 打开 led
}
// 短延时
void delay_short(volatile unsigned int n)
{
while(n--){}
}
// 长延时,在 396MHz 下一次循环大概 1 ms
void delay_ms(volatile unsigned int n)
{
while(n--)
{
delay_short(0x7ff);
}
}
// 打开 led
void led_on(void)
{
GPIO1_DR &= ~(1<<3); // bit3 清零
}
// 关闭 led
void led_off(void)
{
GPIO1_DR |= (1<<3); // bit3 置1
}
int main(void)
{
clk_enable();
led_init();
// led 闪烁
while (1)
{
led_on();
delay_ms(500);
led_off();
delay_ms(500);
}
return 0;
}
main.h
#ifndef __MAIN_H
#define __MAIN_H
// 定义要使用的寄存器
// 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)
#endif
Makefile
objs = main.o start.o
ledc.bin: $(objs)
arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o main.o -o ledc.elf
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.bin
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
%.o: %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
%.o: %.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis
3、烧写验证
I.MX6ULL_MINI_ledc
演示视频中 led 每隔 500 ms 闪烁;