我创建几个c文件,然后直接复制的例程C文件,编译错误Build target 'Target 1' compiling main.c... main
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我创建几个c文件,然后直接复制的例程C文件,编译错误Build target 'Target 1' compiling main.c... main相关的知识,希望对你有一定的参考价值。
直而我创建接工程直接添加例程c文件能又能编译,哪位大神解释下,指导下怎么做
所以造成 P2, SDA 是“未定义的标识符”错误。
你应当把头文件放入当前文件夹。
如果用建项目的方法,还要把头文件加入进项目。 参考技术A 已经提示了 你的项目中没有 i2c.h这个头文件
应噶你使用别人的 代码 没有添加齐全
使用C语言按照GPIO操作流程点亮LED灯
第九章我们使用汇编编写了LED 灯的实验,在实际开发过程中大部分还是使用C 语言,汇编只是用来完成C 语言环境的初始化,本章我们就来实现用汇编完成C 语言环境的初始化,然后用C 语言实现LED 的例程。
C程序版LED例程简介
汇编完成C 语言环境的初始化主要包括内存初始化,设置堆栈指针等等,当这些工作完成以后就可以跳转到C 语言,执行C 程序了,所以我们有两部分事情要做:
1.汇编文件,用来完成C 语言的环境初始化
2.C 语言文件,主要实现我们的业务功能,比如我们本章的点亮LED。
原理图分析
本章用到的硬件资源和第6 章的一样,可以参照第六章的硬件原理分析。
程序编写
本实验对应的例程在光盘资料的:i.MX6UL 终结者光盘资料\\04_裸机例程源码\\2_led_C program 目录下,我们在Ubuntu 系统建立“1_Led_C program”文件夹,然后在“Led_C program”文件夹下建立文件:start.S、main.c、main.h。其中start.S 是汇编文件,main.c 和main.h 是C 语言文件。
我们在前面新建的“strart.S”文件中输入下面的代码:
.global _start /* 全局标号 */
/*
* 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C
* 运行环境。
*/
_start:
/* 进入SVC模式 设置CPU运行模式 也就是设置cpsr寄存器 手册7.1章节有讲*/
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 这两个都是逻辑运算符 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */
ldr sp, =0X80200000 /* 设置栈指针 */
b main /* 跳转到main函数 */
第1 行定义了一个全局标号_start
第6 行是程序的入口、
第8 行到第11 行是设置处理器进入SVC 模式
第12 行通过ldr 指令设置SVC 模式下的sp 指针(0x80200000),i.MX6 ULL 终结者开发板上的内存地址范围是0X80000000~0X90000000(256MB),所以不论是512MB 版本还是256MB 版本的,其内存起始地址都是0X80000000,由于i.MX6ULL 的堆栈是向下增长的,所以SP 指针设置成0X80200000(0X80200000-0X80000000,是2MB 的栈空间,足够我们使用了)。
第13 行是跳转到main 函数(C 语言的入口函数)。
至此汇编部分的程序我们就完成了,主要用来设置处理器在SVC 模式下运行,然后初始化SP 指针,最后跳转到C 程序的main 入口函数。如果大家有接触过三星的S3C2440,S3c6410 或者S5PV210 的处理器,我们在使用内存之前必须先初始化CPU 的内存控制器,所以在他们的汇编文件中一定有内存控制器的初始化代码(比如Uboot 的汇编中)。大家可能会发现我们上面编写的start.S 文件中并没有发现初始化内存控制器的代码,但是却将SVC 模式下的SP 指针设置到了内存的地址范围里面,这样不是有问题吗?大家还记得在第六章我们在编译生成“led.bin”文件以后,通过create_imx 工具在“led.bin”文件添加了一些数据包头吗,也就是DCD 数据,在第六章我们已经讲过DCD 数据里面包含了内存控制器的参数配置了,i.MX6ULL内存固化的Boot ROM 程序会读取DCD 数据中的内存控制器参数,bin 完成内存控制器的初始化配置。
接下来我们开始实现C 语言部分,首先我们打开前面建立的“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)
#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.h 中,我们通过宏的方式定义了要使用到的所有寄存器名称,后面的十六进制数就是寄存器对应的地址,比如GPIO1_DR 寄存器的地址是0X0209C000。然后保存并退出“main.h”。
接下来我们打开“main.c”文件,在里面输入下面的代码:
#include "main.h"
/*
* @description : 使能I.MX6U所有外设时钟
* @param : 无
* @return : 无 这里全部使能 不考虑功耗了 和汇编步骤一样的
*/
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;
}
/*
* @description : 初始化LED对应的GPIO
* @param : 无
* @return : 无
*/
void led_init(void)
{
/* 1、初始化IO复用 */
SW_MUX_GPIO1_IO03 = 0x5; /* 复用为GPIO1_IO03 */
/* 2、、配置GPIO1_IO03的IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 R0/6驱动能力
*bit [0]: 0 低转换率
*/
SW_PAD_GPIO1_IO03 = 0X10B0;
/* 3、初始化GPIO */
GPIO1_GDIR = 0X0000008; /* GPIO1_IO03设置为输出 */
/* 4、设置GPIO1_IO03输出低电平,打开LED0 */
GPIO1_DR = 0X0;
}
/*
* @description : 打开LED灯
* @param : 无
* @return : 无
*/
void led_on(void)
{
/*
* 将GPIO1_DR的bit3清零
*/
GPIO1_DR &= ~(1<<3);
}
/*
* @description : 关闭LED灯
* @param : 无
* @return : 无
*/
void led_off(void)
{
/*
* 将GPIO1_DR的bit3置1
*/
GPIO1_DR |= (1<<3);
}
/*
* @description : 短时间延时函数
* @param - n : 要延时循环次数(空操作循环次数,模式延时)
* @return : 无
*/
void delay(volatile unsigned int n)
{
while(n--){}
}
/*
* @description : 延时函数,在396Mhz的主频下
* 延时时间大约为1ms
* @param - n : 要延时的ms数
* @return : 无
*/
void mdelay(volatile unsigned int n)
{
while(n--)
{
delay(0x7ff);
}
}
/*
* @description : mian函数
* @param : 无
* @return : 无
*/
int main(void)
{
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化led */
while(1) /* 死循环 */
{
led_off(); /* 关闭LED */
mdelay(300); /* 延时大约500ms */
led_on(); /* 打开LED 闪灯 */
mdelay(300); /* 延时大约500ms */
}
return 0;
}
main.c 文件里面一共有7 个函数,下面一个个的看下每个函数的具体功能:
- void clk_enable(void)函数使能CCM_CCGR0-CCM_CCGR6 的时钟(使能所有外设时钟)。
- void led_init(void)函数初始化LED 对应的IO(复用成GPIO,默认下拉,输出低电平)。
- void led_on(void)函数点亮LED(对应GPIO 输出低电平)。
- void led_off(void)函数关闭LED(对应GPIO 输出高电平)。
- void delay_short(volatile unsigned int n)延时函数。
- void mdelay(volatile unsigned int n)毫秒级延时函数。
- int main(void),C 程序主函数。先调用clk_enable()函数完成外设时钟的使能,然后调用led_init()函数完成LED 对应的IO 的初始化,最后进入while(1)死循环,实现LED 每隔300 毫秒亮灭的状态切换。
编译及烧写测试
我们新建Makfile 文件,在里面输入下面的内容:
1 objs := start.o main.o
2 led.bin:$(objs)
3 arm-linux-gnueabihf-ld -Ttext 0X87800000 -o led.elf $^
4 arm-linux-gnueabihf-objcopy -O binary -S led.elf $@
5 %.o:%.s
6 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
7 %.o:%.S
8 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
9 %.o:%.c
10 arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<
11 clean:
12 rm -rf *.o led.bin led.elf
在本Makefile 文件中我们使用到了变量和自动变量,关于Makefile 的变量和自动变量可以参考《跟我一起写Makefile.pdf》文档(光盘资料的:i.MX6UL 终结者光盘资料\\10_其它参考资料目录下)
第1行我们定义了变量objs,该变量包含要生成的led.bin 文件所需要的start.o 和main.o,也就是我们工程的start.S 和main.c 编译生成的“.o”文件。需要我们注意的是start.o 一定要放到最前面,因为后面链接的时候start.o 要在最前面,这样才能保证start.o 在链接完成以后,运行的时候最先执行。
第2行就是默认最终生成的可执行文件“led.bin”,“led.bin”文件依赖于“start.o”和“main.o”两
个文件,如果当前工程目录下没有这两个文件,Make 命令就会在Makefile 中找到相应的规则生成“start.o”和“main.o”。比如“start.s”文件根据第5 行的规则生成,“start.S”根据规则中的第7 行生成,“main.o”根据规则中的第9 行生成。
第3 行使用arm-linux-gnueabihf-ld 进行连接,连接的起始地址是0x87800000,这一行用到了自动变量“$^”,它是指所有依赖文件的集合,也就是变量“objs”,即“start.o”和“main.o”,连接的时候汇编的start.o 要放在最前面,因为程序首先从汇编的“_start”标号处开始执行,因此这一行展开就是:
arm-linux-gnueabihf-ld -Ttext 0X87800000 -o led.elf start.o main.o
第4 行使用arm-linux-gnueabihf-objcopy -O binary -S ledc.elf @ 把 “ l e d . e l f ” 文 件 转 换 成 “ l e d . b i n ” 文 件 , 这 里 也 用 到 了 自 动 变 量 “ @把“led.elf”文件转换成“led.bin”文件,这里也用到了自动变量“ @把“led.elf”文件转换成“led.bin”文件,这里也用到了自动变量“@”,它的意思是目标集合,也就是“led.bin”,那么本行展开相当于:
arm-linux-gnueabihf-objcopy -O binary -S led.elf led.bin
第5 行到第10 行是针对不同后缀名的文件,将其编译成对应的“.o”文件。比如start.s 就会使用第5行的规则生成对应的“start.o”文件,第6 行就是具体执行的命令,这里用到了自动变量“ @ ” 和 “ @”和“ @”和“<”,“$<”的意思是依赖目标集合的第一个文件,比如“start.s”编译成“start.o”,第5 行和第6 行的代码相当于:
start.o:start.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o start.o start.s
第11 行是清理规则,通过“make clean”可以删除编译生成的中间文件以及目标文件。
Makefile 文件我们就分析到这里。
修改Makefile
在上一小节我们已经写好了Makefile 文件,在里面链接的时候我们使用的“arm-linux-gnueabihf-ld -Ttext0X87800000 -o ledc.elf $^”命令,在这条命令里面我们通过“-Ttext”来指定链接地址0x87800000,这样所有的文件都会连接到以0x87800000 为起始地址的区域。有时候我们工程中很多文件需要链接到指定的区域(段),比如Linux 里面初始化函数会放到init 的段里面,所以我们需要能够自定义一些段,这些段的起始地址可以自由指定,同样我们也可以指定一个文件或者一个函数应该存放到哪个段里面。要完成这个功能
我们需要使用链接脚本。链接脚本是用于描述文件如何被连接在一起生成最终的可执行文件。主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件的内存排列,一般我们编译生成的文件都包括text 段,data 段等等。
链接脚本实际上是通过一系列的指令组成的,每个命令是带有参数的关键字或者是对符号的赋值,可以使用分号分隔命令。像文件名之类的字符串可以直接输入,也可以使用“*”通配符,我们可以使用“SECTIONS”来定义一个段,在里面描述文件的内存分配。我们的代码编译出来,一般都会包括在test、data、bss 和rodata 这四个段里面,比如我们的代码要连接到0x80000000 这个地址,数据连接到0x81000000 这个地址,我们可以编写连接脚本,如下:
1 SECTIONS{
2 . = 0X80000000;
3 .text : {*(.text)}
4 . = 0X81000000;
5 .data ALIGN(4) : { *(.data) }
6 .bss ALIGN(4) : { *(.bss) }
7 }
第1 行通过“SECTIONS”,后面加一个大括号,这个大括号和第7 行大括号的是一对。有点类似C 语言中的函数。
第2 行对“.”的特殊符号赋值,“.”在链接脚本里面叫做定位计数器,默认定位计数器是0。我们要求代码连接到以0x80000000 开始的地址,因此这一行给“.”赋值0x80000000,表示以0x80000000 地址开始,后面的文件或者段都会以0x80000000 位起始地址开始连接。
第3 行的“.text”是段名,后面的冒号是语法要求。冒号后面的大括号里面可以填上要链接到“.text”这个段里面的所有文件,“(.text)”中的“”是通配符,表示所有输入文件的.text 段都放到“.text”中。
第4 行,我们的要求是数据放到0X81000000 开始的地方,所以我们需要重新设置定位计数器“.”,将其改为0X81000000。如果不重新设置的话会怎么样?假设“.text”段大小为0X100,那么接下来的.data段开始地址就是0X80000000+0X100=0X80000100,所以我们必须调整定位计数器为0X81000000。
第5 行跟第3 行一样,定义了一个名为“.data”的段,然后所有文件的“.data”段都放到这里面。这一行多了一个“ALIGN(4)”,这是什么意思呢?这是用来对“.data”这个段的起始地址做字节对齐的,ALIGN(4)表示4 字节对齐。也就是说段“.data”的起始地址要能被4 整除,一般常见的都是ALIGN(4)或者ALIGN(8),也就是4 字节或者8 字节对齐。
第6 行定义了一个“.bss”段,所有文件中的“.bss”数据都会被放到这个里面,“.bss”数据就是那些定义了但是没有被初始化的变量。
上面这些就是连接脚本的基本语法格式,我们按照这个语法来编写我们本实验的链接脚本,我们要求链接起始地址为0x87800000,start.o 要被链接到最开始的位置(0x87800000),根据要求我们在工程目录下建立文件“imx6ull.lds”文件,然后在里面输入下面的脚本:
1 SECTIONS{
2 . = 0X87800000;
3 .text :
4 {
5 start.o
6 main.o
7 *(.text)
8 }
9 .rodata ALIGN(4) : {*(.rodata*)}
10 .data ALIGN(4) : { *(.data) }
11 __bss_start = .;
12 .bss ALIGN(4) : { *(.bss) *(COMMON) }
13 __bss_end = .;
14 }
上面链接脚本首先第1 行通过“SECTIONS”定义一个关键字。
然后第2 行设置定位计数器为0x87800000(我们链接的起始地址),
第3 行是定义text 段,
第5 行是指定开始链接的第一个文件“start.o”(首先要执行改文件,所以连接在最开始处)。
第6 行是链接的main.o,其实main.o 链接的位置无所谓,所以可以不用写出来,由编译器自行决定链接位置。
第9 行,第10 行设置rodata、data 按照4 字节对齐。
第11、13 行有“__bss_start”和“__bss_end”两个符号,分别对两个符号进行赋值,其值为定位符“.”,这两个符号用来保存.bss 段的起始地址和结束地址。前面说了.bss 段是定义了但是没有被初始化的变量,我们需要手动对.bss 段的变量清零的,因此我们需要知道.bss 段的起始和结束地址,这样我们直接对这段内存赋0 即可完成清零。
通过第11、13 行代码,.bss 段的起始地址和结束地址就保存在了“__bss_start”和“__bss_end”中,我们就可以直接在汇编或者C 文件里面使用这两个符号。然后我们保存并退出imx6ul.lds 文件,如果使用这个链接文件,我们需要修改Makefile 文件,打开当前工程的Makefile 文件,将里面的“arm-linux-gnueabihf-ld -Ttext 0X87800000 -o led.elf $^ ”内容改成“arm-linux-gnueabihf-ld -Timx6ul.lds -o led.elf $^”。实际就是将-T 后面的0x87800000 改成imx6ul.lds,表示使用imx6ul.lds 这个链接脚本,修改完成以后保存并退出Makefile 文件。
编译烧写验证
在上一章节我们编写好了工程的所有源码以及Mkafile 文件、链接文件,然后我们在终端输入make 命令编译该工程,运行结果如图所示:
通过上图我们看到生成了“led.bin”文件,然后我们还需要给“led.bin”文件添加DCD 的数据包头,通过ssh 上传“create_imx”工具(光盘资料的“i.MX6UL 终结者光盘资料\\01_开发及烧写工具\\2.裸机镜像制作工具”)到本工程目录下,使用“chmod 777 create_imx”命令,修改“create_imx”具有执行权限,然后在当前目录下运行“./create_imx led.bin”命令,生成最终的镜像文件:bare.imx,如图所示:
然后把生成的“bare.imx”镜像文件通过ssh 传输到windows 系统下,然后使用MFG 烧写工具烧写“bare.imx”文件到开发板,烧写完成后,开发板设置成flash 启动,然后给开发板上电,我们可以看到开发板上的LED 开始闪烁了。
以上是关于我创建几个c文件,然后直接复制的例程C文件,编译错误Build target 'Target 1' compiling main.c... main的主要内容,如果未能解决你的问题,请参考以下文章