int a = 100; * (&a) = 100; 0xc001c000 // 常量 (int*)0xc001c000 // 地址 * ((int*)0xc001c000) = 0x100; // 写入数据,按int-4字节写入0x100volatile 的作用,以及哪种场合需要使用该关键字?(课后查资料)>>表示'一个变量也许会被后台程序意想不到的修改'。变量如果加了volatile修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。volatile可以保证对特殊地址的稳定访问。>>寄存器地址要加volatile修饰,主要是因为寄存器里面的值是随时变化的。 我们读取数据的时候,CPU直接到内存里面取值,而不是到cache里面。 1) 并行设备的硬件寄存器; 2) 中断服务子程序中会访问到的非自动变量; 3) 多线程应用中被及格任务共享的变量;例如:
volatile int i = 10; int a = i; // 其他代码,并未告诉编译器,对i进行过操作 int b = i;"volatile 指出i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读,这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说 volatile 可以保证特殊地质的稳定访问。"// 访问特殊功能寄存器的时候需要加 volatile 关键字,正确的写法: a = * ((volatile unsigned int*)0xc001c000); // 下面此条做法,不但将bit12清0,也导致其他的bit被清0了,错误!! * ((volatile unsigned int*)0xc001c000) = 0x00;3.2 位运算 假设需要将 0xc001c020 寄存器的[25:24]bit位设置为 01 ,* ((volatile unsigned int*)0xc001c020) &= ~(0x03000000); // 第一步 // 32bit : xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx // 0x03000000 0000 0011 0000 0000 0000 0000 0000 0000 // ~(0x03000000) 1111 1100 1111 1111 1111 1111 1111 1111 // &= ~(0x03000000) xxxx xx00 xxxx xxxx xxxx xxxx xxxx xxxx * ((volatile unsigned int*)0xc001c020) |= 0x01000000; // 第二步 // 32bit : xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx // 01 ([25:24]bit) // 0x0______01_0____0____0____0____0____0 "实际做法:" * ((volatile unsigned int*)0xc001c020) &= ~(3<<24); // 第一步 * ((volatile unsigned int*)0xc001c020) |= (1<<24); // 第二步<tips>$:'file a.out// file 命令可以用来分析文件的一些属性。x86-64 是x86的64位平台运行。4. 编码 vi led.c/** 代码演示 **/#define GPIOC_OUT *((volatile unsigned int*)0xc001c000)#define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)#define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)void delay (unsigned int); void led_test (void) { // 配置对应管脚为GPIO功能 GPIOC_ALTFN0 &= ~ (3 << 24); // clear bit 24,25 GPIOC_ALTFN0 |= (1 << 24); // set bit 24 // 选择为输出功能 GPIOC_OUTENB |= (1 << 12); // OUTPUT while (1) { // 亮 - 输出低电平 GPIOC_OUT &= ~ (1 << 12); // clear bit 12 delay (0x1000000); // 灭 - 输出高电平 GPIOC_OUT |= (1 << 12); // set bit 12 delay (0x1000000); }}// delay函数的实现不能放前面,只能先声明后实现void delay (unsigned int n) { unsigned int i = 0; for (i = n; i != 0; i--); // CPU执行空操作来耗时}5. arm编译器
5.1 安装arm交叉编译器>>位置:/home/tarena/workdir/arm-linux-gcc/ 'arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz 权限不够时解决方法: 1> $:' sudo ... 2> $:' sudo chmod +w /opt/ -R 3> $:' sudo chmod 777 /opt/ -R // 将交叉编译器tar包文件拷贝到/opt/下,在此文件夹下解压缩 $:'cd ~/opt/ $:'sudo cp ~/workdir/arm..<table>/arm... .tar.gz . $:'sudo tar xvf *.gz>>位置:/opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/bin/ 'arm-cortex_a9-linux-gnueabi-gcc $:'vi ~/.bashrc>>添加:<shift + g> // 添加到文件尾 'PATH=$PATH:/opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/bin $:'source ~/.bashrc5.2 编译程序希望编译出来的程序不使用共享库 (因为是裸板) 1)编译 $:'arm-cortex_a9-linux-gnueabi-gcc -c -nostdlib led.c -o led.o 2)连接 $:'arm-cortex_a9-linux-gnueabi-ld -nostdlib -nostartfiles -Ttext=0x48000000 -eled_test led.o -o led // -no stdlib 不连接标准库文件 // -no startfiles 不连接系统标准启动文件 // -Ttext 指定代码段的起始地址 // -e 指定入口点函数,默认找_start 3)去掉附加信息,生成二进制文件 $:'arm-cortex_a9-linux-gnueabi-objcopy -O binary led led.bin $:' file led.bin<tips>$:'arm-cortex_a9-linux-gnueabi-readelf -d a.out// 显示可执行文件a.out文件中所需的共享库.so信息$:'arm-cortex_a9-linux-gnueabi-objdump -S led// 反汇编的命令,查看文件的具体的函数及信息6. 下载运行: X6818# 命令行 $:' cp led.bin /tftpboot/ X6818#:'tftp 48000000 led.bin // uboot下数字都识别为十六进制 X6818#:' go led.bin<tips>执行tftp 48000000 led.bin 时 Ubuntu-server报 T..T..T...时需重启服务器:$:'sudo /etc/init.d/tftpd-hpa restart补充:编程错误>>语法错误: '只关注只解决第一个错误。逻辑错误: ' printf / gdb 1. 是不是硬件问题? env/led.bin放进去,灯亮硬件没问题(或者换到别人电脑上试一试) 2. 如果是软件问题,怎么查? 修改led.c的逻辑 让对应管脚一直输出高电平 拿万用表量一下对应的管脚上是否是高电平。env/BCompare-3.3.4.14431.zip // 工具,对比文件夹和文件里面不同之处<tips>vi中的命令模式下:e ../xxx.c // 在vi中直接打开别的文件练习: 1. LED1和LED2交替闪烁。(理清楚今天的整个流程,看手册,理流程) 2. beep蜂鸣器的控制管脚相关数据准备:'led1 - GPIOC12 - W15 - Function1 GPIOCALTFN0 - 0xc001c020 - bit[25:24] - 01=Function1 (I/O功能) GPIOCOUTENB - 0xc001c004 - bit[12] - 1=Output Mode (输出功能) GPIOCOUT - 0xc001c000 - bit[12] - 1=High Level (0/1 低高电平)'led2 - GPIOC7 - AE21 - Function1 GPIOCALTFN0 - 0xc001c020 - bit[15:14] - 01 GPIOCOUTENB - 0xc001c004 - bit[7] - 1 GPIOCOUT - 0xc001c000 - bit[7] - 1'led3 - GPIOC11 - W14 - Function1 GPIOCALTFN0 - 0xc001c020 - bit[23:22] - 01 GPIOCOUTENB - 0xc001c004 - bit[11] - 1 GPIOCOUT - 0xc001c000 - bit[11] - 1'led4 - GPIOB26 - MCU_SPI_WP AC25 - Function1 (暂略)/** 代码演示 **/#define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)#define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)#define GPIOC_OUT *((volatile unsigned int*)0xc001c000)void delay (unsigned int);void led_run (void) { // 配置GPIOC管脚 // led1 25:24 GPIOC_ALTFN0 &= ~ (3 << 24); GPIOC_ALTFN0 |= (1 << 24); // led2 15:14 GPIOC_ALTFN0 &= ~ (3 << 14); GPIOC_ALTFN0 |= (1 << 14); // led3 23:22 GPIOC_ALTFN0 &= ~ (3 << 22); GPIOC_ALTFN0 |= (1 << 22); // 设置输出功能 GPIOC_OUTENB |= (1 << 12); GPIOC_OUTENB |= (1 << 11); GPIOC_OUTENB |= (1 << 7); for (;;) { GPIOC_OUT &= ~ (1 << 12); // led1 delay (0x800000); GPIOC_OUT &= ~ (1 << 11); // led2 delay (0x800000); GPIOC_OUT &= ~ (1 << 7); // led3 delay (0x800000); GPIOC_OUT |= (1 << 12); delay (0x800000); GPIOC_OUT |= (1 << 11); delay (0x800000); GPIOC_OUT |= (1 << 7); delay (0x800000); } }void delay (unsigned int n) { unsigned int i; for (i = n; i; --i);}2. beep蜂鸣器的控制' beep蜂鸣器 - PWM2[GPIOC14] - AD12 - Function2 GPIOCALTFN0 - 0xc001c020 - bit[29:28] - 10=Function2 (I/O功能) GPIOCOUTENB - 0xc001c004 - bit[14] - 1=Output Mode (输出功能) GPIOCOUT - 0xc001c000 - bit[14] - 1=High Level (0/1 低高电平)
/** 代码演示 **/#define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)#define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)#define GPIOC_OUT *((volatile unsigned int*)0xc001c000)void delay (unsigned int);void beep_run (void) { // 配置GPIO管脚 GPIOC_ALTFN0 &= ~(3 << 28); GPIOC_ALTFN0 |= (2 << 28); // 设置输出功能 GPIOC_OUTENB |= (1 << 14); for (;;) { GPIOC_OUT &= ~(1 << 14); // 低电平 - 鸣叫 delay (0x2000000); GPIOC_OUT |= (1 << 14); // 高电平 - 不叫 (无延时和停止蜂鸣效果) delay (0x2000000); } }void delay (unsigned int n) { unsigned int i; for (i = n; i; --i);}
新闻热点
疑难解答