提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
本章节表述,arm和的基础理论和arm裸机编程。
ARM和x86
他们是主要的竞争对手,arm主打32位、低功耗、嵌入式移动电子产品
x86是64位,主打高性能,桌面电子产品,功耗较高。
CISC与RISC设计理念
CISC(复杂指令集)用叠加内部电路的方式,让一条指令完成多个功能。功耗大,设计完了。
RISC(精简指令集)只提供简单的基本指令,目标功能留给编程人员来完成。灵活功耗小
thumb指令集
也是ARM的一种指令集,16位,也就是说ARM核支持两种指令集。
ARM系列
A: 民用 高端领域, 手机,平板 arm7 arm12 A7 A9 A78 A53
R: realtime实时系列, r5 r9 军工 汽车 航天
M: MCU--单片机领域 m3 m4 m7 stm32 NXP
内核=运算器+控制器+寄存器
arm一共有37个寄存器分别是
通用寄存器(r0-r15)
特殊的通用寄存器(r13-r15)
r13(sp)“栈指针” //存储的是栈顶地址
r14(lr)pc的备胎
r15(pc)记录下一条指令
状态寄存器(cpsr、spsr)
cpsr记录状态
spsr(cpsr的备胎)
不同的工作模式就是权限不一样,提供的寄存器不一样
User:大部分仼务执行在这种模式,权限低
Systen:与上面的模式一样
FIQ:当一个高优先级(fast)中断产生时将会进入这种模式
IRQ:当一个低优先级( normal)中断产生时将会进入这种模式
Supervisor(SⅤC):当复位或软中断指令执行时将会进入这种模式 (内核空间、内核态)
Abort:当存取异常时将会进入这种模式
Undef:当执行未定义指令时会进入这种模式
三星4412
由于我们写的是基于arm的代码,但linux的gcc只能编译x86的代码,所以需要一个特有的环境编写arm程序。
(1)复制arm gcc编译器的压缩文件到linux下,后解压。注意不能复制到共享文件路径下,要复制到纯linux的路径。解压命令tar -xvf 文件名
(2)安装32位支持库。由于arm是32位的,linux的64位,所以需要下载一个32位的支持库。下载安装命令apt-get install lib32z1。注意不能直接复制粘贴该命令,要纯手打。
(3)添加环境变量(安装arm gcc)。找到压缩后的arm gcc编译器文件夹,打开,bin目录,里面包含arm gcc的所有命令,pwd查看绝对路径,复制;打开环境变量的配置文件,命令vi ~/.bashrc;跳到最后一行添加一个指令export PATH=$PATH:刚刚复制的绝对路径,保存退出就可以了。
编译好了之后生成一个.bin文件,就是我们需要的二进制文件,我们需要吧这个二进制文件下载到开发板里面。
(1)下载一安装一个个串口调试助手。
(2)开发板串口连接电脑,打开电脑的设备管理器可以查看连接的端口,现在是COM4端口。
(3)串口调试助手调试下载。
具体过程看图。
注意每一次连接开发板一开机就要按下enter键
配置几个寄存器
GPxxCON gpio模式控制寄存器
GPxxDAT jpio数据寄存器
按键控制 led 的汇编代码
.text
bl Init
while:
@@监听按键:
ldr r7,=0x11000C24
ldr r6,[r7] @@读取按键的值
bic r6,#0xfffffffd
cmp r6,#0 @@按键输入与0比较
blne led_off @@如果不等于0(没按键按下) LED灭
bleq led_on @@如果 等于0(有按键按下) LED亮
b while
Init:
@@GPXCON: 0x11000C20按键配置
ldr r0,=0x11000C20
ldr r1,[r0] @@r2=*r0
bic r1,r1,#0xF0000000 @@清零28-31,0x0
str r1,[r0]
@@ led配置
ldr r0,=0x11000c40
ldr r2,[r0] @@r2=*r0
bic r2,r2,#0xF0000000 @@清零28-31
orr r2,r2,#0x10000000
str r2,[r0]
mov pc,lr
led_off:
@@熄灭led
ldr r2,[r0]
bic r2,r2,#0x80
str r2,[r0]
mov pc,lr
led_on:
@@点亮led
ldr r0,=0x11000c44
ldr r2,[r0]
orr r2,r2,#0x80
str r2,[r0]
mov pc,lr
.end
UART全称通用异步收发传输
空闲位:开始时通信双方空闲,彼此拉高
起始位:当一端从高电平变到低电平,表明他要发送数据了
数据位:比如要发送’a’字符,其实就是发送a的ASCII码值,变成二进制其实就是一段高高低低的波形,ASCII码是八位,但全球也有其他地方使用5、6、7位的编码,所以数据位是5~8位
校验位:分为奇校验、偶校验、无校验,奇校验也就是一帧数据之和是奇数,则校验位为0,一帧数据之和为偶数,校验位就是1;偶校验也是如此,校验模式和硬件有关,现在的uart一般不支持校验位,所以一般是无校验。
停止位:电平拉高,一帧数据发送结束。
// 用户输入“on” led灯亮 输入“off” led灭
#define GPX2CON *((volatile long *)0x11000c40)
#define GPX2DAT *((volatile long *)0x11000c44)
#define GPA1CON *((volatile long*)0x11400020)
#define ULCON2 *((volatile long*)0x13820000)//配置数据位
#define UCON2 *((volatile long*)0x13820004)//配置论转模式
#define UTRSTAT2 *((volatile long*)0x13820010)//buff
#define UTXH2 *((volatile long*)0x13820020)//发送数据缓存
#define URXH2 *((volatile long*)0x13820024)//接收数据缓存
#define UBRDIV2 *((volatile long*)0x13820028)//配置波特率整数部分
#define UFRACVAL2 *((volatile long*)0x1382002C)//配置波特率小数部分
void Uart_Init(void); //uart初始化
void putc(char ch); //uart发送一个字符
void delay(int t); //延时函数
void memset(char *ch,char val,int len); //字符串清空
void puts(char *str); //uart发送字符串
int mystrcmp(char*pstr1,char *pstr2); //字符串比较
int mystrlen(char *pstr); //计算字符串长度
char rxbuf[128];
int main()
{
Uart_Init();
puts("you input 'on'led light,you input 'off'led down\r\n");
GPX2DAT=0X00;
int i;
memset(rxbuf,0,sizeof rxbuf );
int index = 0;
while(1){
while( ( UTRSTAT2 & (1<<0) )==0 );
rxbuf[index] = URXH2;
putc(rxbuf[index]);
if((mystrcmp(rxbuf,"on"))==0)GPX2DAT=0XFF;
else if((mystrcmp(rxbuf,"off"))==0)GPX2DAT=0X00;
if(rxbuf[index] == '\r'||rxbuf[index]=='\n'){ //发现用户回车,则一切从头来过
puts(rxbuf);
index=0;
memset(rxbuf,0,sizeof(rxbuf));
puts("\r\n");
continue;
}
index++;
}
return 0;
}
void Uart_Init(void)
{
//uart的io配置
GPA1CON&=~(0xF<<4)|(2<<4);
GPA1CON&=~(0xF<<0)|(2<<0);
//波特率设置为115200
UBRDIV2=53;
UFRACVAL2=4;
ULCON2|=3; //8位数据位
ULCON2&=~(1<<2); //一位停止位
ULCON2&=~(1<<5); //无校验位
ULCON2&=~(1<<6); //无红外
UCON2&=~(3<<0)|(1<<0);
UCON2&=~(3<<2)|(1<<2);
UCON2&=~(1<<5);
GPX2CON|=(1<<28); //LED灯配置为输出
}
void delay(int t)
{
while(t--)
{
int ms=0xfff;
while(ms--);
}
}
void putc(char ch)
{
while( ( UTRSTAT2 & (1<<1) ) == 0 ){ };
UTXH2 = ch;
}
void puts(char *str)
{
int i=0;
while(str[i] !='\0'){
putc(str[i]);
i++;
}
}
void memset(char *ch,char val,int len)
{
int i;
for(i=0;i<len;i++){
ch[i]=val;
}
}
int mystrcmp(char*pstr1,char *pstr2)
{
int i;
int length1=0;
int length2=0;
length1=mystrlen(pstr1);
length2=mystrlen(pstr2);
if (length1!=length2)
{
return -1;
}
else
{
int flag=1;
for (i=0;i<length1;i++)
{
if (pstr1[i]!=pstr2[i])
{
flag=0;
break;
}
}
if (flag==1)
{
return 0;
}
else
{
return -1;
}
}
}
int mystrlen(char *pstr)
{
int num=0;
while(*pstr!='\0')
{
num++;
pstr++;
}
return num;
}
处理器有五个timer,分别是0~4,其中0到3具有pwm功能。
PLCK:一个时钟信号,100MHZ,还有其他的时钟脉冲FCLK 是内核频率;HCLK是液晶、内存的相关频率;PCLK是串口等外部 ,速度较慢设备的频率!
由于脉冲频率是固定的,且频率过高,那么如何得到我们需要的频率?就需要经过两次分频,第一次是1~255分频,由我们配置寄存器设置具体值,第二次只能选择1、2、4、8、16分频,也是使用寄存器配置。
上述过程只能得到一个方波脉冲,没有什么软用。我们要利用他完成计时功能,就需要经过一个计数器,我们给与计数器一个初始值(记作count),每次脉冲来一个触发的边沿信号,count–,当count==0,又重新开始。就能完成一个定时功能,比如过来的脉冲周期是50um一次,我设置初始值为100,那么每一次过来一个脉冲,计数器就会减一,直到减到0,那么我们就能知道50毫秒的时间。
上述过程,最后得到的是一个锯齿波,众所周知pwm是一个矩形波。那么如何得到一个矩形波?我们要用到一个比较器,输入一个比较值(记作cmp),当cmp
最后如果波形要翻转,就可以通过反相器通道,完成信号的取反。
#define GPD0CON (*(volatile unsigned int *)0x114000A0) //蜂鸣器
#define TCFG0 (*(volatile unsigned int *)0x139D0000) //一级分频
#define TCFG1 (*(volatile unsigned int *)0x139D0004) //二级分频
#define TCNTB0 (*(volatile unsigned int *)0x139D000C) //最大计数值
#define TCMPB0 (*(volatile unsigned int *)0x139D0010) //比较值
#define TCON (*(volatile unsigned int *)0x139D0008) //timer配置
void PWM_Init();
int main()
{
PWM_Init();
while(1);
return 0;
}
void PWM_Init(){
GPD0CON=GPD0CON&~(0XF)|2;
//GPD0CON&=~(0XF)|2;
//GPD0CON|=2;
TCFG0&=~(0xff);
TCFG1&=~(0XF);
TCFG0|=124; //一级分频125
TCFG1|=3; //二级分配1/8
//TCFG0&=~(0XFF)|124;
//TCFG1&=~(0XF)|3;
TCNTB0=100; //最大计数值
TCMPB0=30; //比较值
TCON|=(1<<3);
TCON|=(1<<1); //更新
TCON&=~(1<<1); //关闭更新
TCON|=(1<<0); //开始计数
}
#define GPA1CON *((volatile long*)0x11400020)//buzz
#define ULCON2 *((volatile long*)0x13820000)//数据位停止位校验模式
#define UCON2 *((volatile long*)0x13820004)//轮转发
#define UTRSTAT2 *((volatile long*)0x13820010)//buff发送完
#define UTXH2 *((volatile long*)0x13820020)//发送寄存器
#define URXH2 *((volatile long*)0x13820024)//接受寄存
#define UBRDIV2 *((volatile long*)0x13820028)//波特率配置整数部
#define UFRACVAL2 *((volatile long*)0x1382002C)//波特率配置小数部
//===========实时时钟部分==============
#define RTCCON *(volatile unsigned int *)0x10070040 //写入,停止写入
#define BCDSEC *(volatile unsigned int *)0x10070070 //bcd码 8421
#define BCDMIN *(volatile unsigned int *)0x10070074
#define BCDHOUR *(volatile unsigned int *)0x10070078
#define BCDDAYWEEK *(volatile unsigned int *)0x10070080
#define BCDDAY *(volatile unsigned int *)0x1007007c
#define BCDMON *(volatile unsigned int *)0x10070084
#define BCDYEAR *(volatile unsigned int *)0x10070088
#define uint unsigned int
#define uchar unsigned char
void delay(uchar t)
{
while(t--)
{
uint i=0xffff;
while(i--);
}
}
void gpio_init()
{
GPA1CON&=~(0xf<<0); GPA1CON|=(0X2<<0);
GPA1CON&=~(0xf<<4); GPA1CON|=(0X2<<4);
}
void uart_init()
{
ULCON2|=0X3; //数据位8
ULCON2&=~(1<<2); //停止位1
ULCON2&=~(1<<5); //无校验
ULCON2&=~(1<<6); //无红外
UBRDIV2|=53;
UFRACVAL2|=4;
UCON2&=~(3<<0)|(1<<0);
UCON2&=~(3<<2)|(1<<2);
UCON2&=~(1<<5);
}
void send_ch(char ch)
{
while(!(UTRSTAT2&=(1<<2)));
UTXH2=ch;
}
void send_str(char *str)
{
int i=0;
for(;str[i]!='\0';i++)
{
send_ch(str[i]);
}
}
void rtc_int_settime()
{
RTCCON = 0x1; //开启
BCDSEC = 0x10;
BCDMIN = 0x24;
BCDHOUR = 0x20;
BCDDAYWEEK = 0X04;
BCDDAY = 0X19;
BCDMON = 0X08;
BCDYEAR = 0X21;
RTCCON &= ~0x1; //关闭
}
void get_time()
{
int year= BCDYEAR; //年
int mon = BCDMON; //月
int day = BCDDAY; //日
int week= BCDDAYWEEK; //周
int hour= BCDHOUR; //时
int min = BCDMIN; //分
int sec = BCDSEC; //秒
send_str("20");
time_send(year);
send_ch('/');
time_send(mon);
send_ch('/');
time_send(day);
send_ch(' ');
time_send(week);
send_ch(' ');
time_send(day);
send_ch(':');
time_send(min);
send_ch(':');
time_send(sec);
send_ch('\n');
send_ch('\r');
}
void time_send(int time)
{
char ge=(time&0xf)+'0';
char shi=((time>>4)&0xf)+'0';
send_ch(shi);
send_ch(ge);
}
int main()
{
gpio_init();
uart_init();
rtc_init();
while(1)
{
get_time();
delay(0xff);
}
}
//=============脉宽调制========================
#define GPD0CON *(volatile long *)0x114000A0 //蜂鸣器
#define TCFG0 *(volatile long *)0x139D0000
#define TCFG1 *(volatile long *)0x139D0004
#define TCON *(volatile long *)0x139D0008
#define TCNTB0 *(volatile long *)0x139D000C
#define TCMPB0 *(volatile long *)0x139D0010
//==========模数转换====================
#define ADCCON *(volatile long*)0x126C0000
#define ADCDAT *(volatile long*)0x126C000c
#define ADCMUX *(volatile long*)0x126C001c
//============LED灯===================
#define GPX2CON *((volatile long *)0x11000c40) //第一个led 灯,,GPX2_7
#define GPX2DAT *((volatile long *)0x11000c44)
#define GPX1CON *((volatile long *)0x11000C20) //第二个LED 灯,,GPX1_0
#define GPX1DAT *((volatile long *)0x11000C24)
#define GPF3CON *((volatile long *)0x114001E0) //第三个LED灯,, GPF3_4
#define GPF3DAT *((volatile long *)0x114001E4)
//============uart模块==================
#define GPA1CON *(volatile long*)0x11400020
#define ULCON2 *(volatile long*)0x13820000
#define UCON2 *(volatile long*)0x13820004
#define UTRSTAT2 *(volatile long*)0x13820010
#define UTXH2 *(volatile long*)0x13820020
#define URXH2 *(volatile long*)0x13820024
#define UBRDIV2 *(volatile long*)0x13820028
#define UFRACVAL2 *(volatile long*)0x1382002C
char yf[] = {0,191,170,151,143,127,113,101}; //乐谱
char two_tiger_song[] = {1,2,3,1,1,2,3,1,3,4,5,5,3,4,5,5,5,6,5,4,3,1,5,6,5,4,3,1,1,5,1,1, 1,5,1,1};
char one_star_song[] = {1,1,5,5,6,6,5,5,4,4,3,3,2,2,1,1,5,5,4,4,3,3,2,2,5,5,4,4,3,3,2,2,1,1,5,5,6,6,5,5,4,4,3,3,2,2,1,1};
char goto_school_song[] = {1,1,3,1,5,6,6,1,6,5,6,6,1,5,6,5,6,5,3,5,3,1,2,3,1};
char buf[12]; //转换后的电压字符串
void Led_Init()
{
GPX2CON&=~(0xf<<28); GPX2CON|=(1<<28); //LED1灯配置为输出
GPX1CON&=~(0XF<<0); GPX1CON|=(1<<0);
GPF3CON&=~(0XF<<16); GPF3CON|=(1<<16);
}
void pwm_init()
{
GPD0CON &= ~0xF; GPD0CON |= 0x2;
TCFG0 &= ~0xFF; TCFG0 |= 124;
TCFG1 &= ~0xF; TCFG1 |= 3;
TCNTB0 = 100; TCMPB0 = 30;
TCON &= ~(1<<2); TCON |= 1<<3;
TCON |= 1<<1; TCON &= ~(1<<1);
}
void adc_init()
{
ADCCON &= ~(1<<0);
ADCCON |= 1<<1;
ADCCON &= ~(1<<2);
ADCCON &= ~(0xFF<<6); ADCCON |= 19<<6;
ADCCON |= 1<<14;
ADCCON |= 1<<16;
ADCMUX &= ~0xF; ADCMUX |=3;
//启动ADC
int reg = ADCDAT;
}
void uart2_init()
{
//配置管脚为 串口模式
GPA1CON = GPA1CON & ( ~0xF );
GPA1CON |= 1<<1 ;
GPA1CON &= ~(0xF<<4) ;
GPA1CON |= 1<<5;
// 设置停止位 数据位 校验位
ULCON2 |= 0x3;
ULCON2 &= ~(1<<2);
ULCON2 &= ~(7<<3);
ULCON2 &= ~(1<<6);
UCON2 &= ~3; UCON2 |= 1<<0;
UCON2 &= ~(3<<2); UCON2|= 1<<2;
UCON2 &= ~(1<<5);
UBRDIV2 = 53; UFRACVAL2 = 4;
}
void putc(char ch)
{
while( ( UTRSTAT2 & (1<<1) ) == 0 ){ };
UTXH2 = ch;
}
void puts(char *str)
{
int i=0;
while(str[i] !='\0'){
putc(str[i]);
i++;
}
}
//============选择被点亮的led=======
void which_led_light(int led_lable)
{
if(led_lable==1)
{
GPX2DAT|=(1<<7); GPF3DAT&=~(1<<4);GPX1DAT&=~(1<<0); //第一个灯亮,其他灯灭
}
else if(led_lable==2)
{
GPX1DAT|=(1<<0); GPX2DAT&=~(1<<7);GPF3DAT&=~(1<<4); //第二个灯亮,其他灯灭
}
else if(led_lable==3)
{
GPF3DAT|=(1<<4); GPX2DAT&=~(1<<7);GPX1DAT&=~(1<<0); //第三个灯亮,其他灯灭
}
}
int adc_read()
{
while(ADCCON & (1<<15) ==0);
return ADCDAT & 0xFFF;
}
void pwm_start()
{
TCON |=1<<0;
}
void pwm_stop()
{
TCON &= ~(1<<0);
}
void msleep(int msec)
{
while(msec--){
int mval = 0xFFF;
while(mval--);
}
}
//========播放音乐,实时监控,旋钮变化========
// 旋钮电压变化满足切换歌曲的时候,跳出循环,并打印提示信息
void play_music(char * strs,int len,char mode)
{
int i;
char yy;
puts("Start playing\r\n");
puts(buf);
puts("\r\n");
for(i=0;i<len;i++){
char yin = strs[i]; // 1 2 3 1 ...
TCNTB0 = yf[ yin ];
TCMPB0=TCNTB0/2;
if(btn_mode()!=mode)break;
msleep(500);
}
}
//========旋钮的功能定义,并转换电压值=========
int btn_mode()
{
int reg = adc_read();
int mv = reg*1800/4095;
buf[0] = mv/1000 + '0';
buf[1] = mv/100%10 + '0' ;
buf[2] = mv/10%10 +'0';
buf[3] = mv%10 + '0';
buf[4] = ' '; buf[5] = 'm';buf[6] = 'V';
buf[7] = '\r'; buf[8] = '\n'; buf[9] = '\0';
if(mv>=0&&mv<600)return 1;
else if(mv>=601&&mv<1200)return 2;
else if(mv>=1201&&mv<=1800)return 3;
}
int main()
{
uart2_init(); //串口初始化
Led_Init(); //led初始化
pwm_init(); //pwm初始化
adc_init(); //adc初始化
pwm_start(); //pwm开始
btn_mode(); //读取旋钮
puts("Welcome to the player,Change the knob,Switch the music\r\n");
//==========旋钮读取================
while(1)
{
int flag=btn_mode();
if(flag==1) //模式1
{ which_led_light(1);
play_music(two_tiger_song,sizeof(two_tiger_song),flag);
}
else if(flag==2) //模式2
{ which_led_light(2);
play_music(one_star_song,sizeof(one_star_song),flag);
}
else if(flag==3) //模式3
{ which_led_light(3);
play_music(goto_school_song,sizeof(goto_school_song),flag);
}
}
return 0;
}
上个星期我经历了人生中第一次应聘,下面我娓娓道来。
首先介绍一下企业情况。奔图电子,搞激光打印机的,据说很牛逼,有自己的技术专利等等,能够突破老美的卡脖子之类的。当然这不是我们该关心的,我们关心的是钱多不多?那里好找女朋友吗?彩礼多不多?显然这不是一家特别吸引人的公司,第一在珠海,薪资六到八千(转正之后),很多人跟我一样的,来这就是为了高薪;第二、公司男员工居多…但听着技术总监搁哪吹牛我还是有点心动的,比如搞几年能在当地买房子有一点优惠,刚进去有师父带,还是很不错,但后面我又查了一下,很多公司都有这种优惠,在住房还有人才培养方面,大一点的公司基本上都是这样的,还有什么落户政府人才津贴这些,但能不能落户都还不知道。还有一个我对该公司很介意的是,这个公司居然要求英语四级,卡脖子了。
虽然自己并不是很满意这个公司,还有英语四级可能会卡脖子,但我还是怀着试试的态度投了简历。说一说这个简历,我大部分内容都是抄那些师兄师姐的简历内容,也就是吹牛吧,我估摸着大概也不会要我的简历。然鹅呀,他们选中了我的简历,宣讲结束几分钟调完简历,就立刻初面了。我心里想着,自己没白穿这身西装,如果自己穿了西装,结果企业没选中我的简历,还是有点尴尬,所以为了避免这种尴尬,我想着,我应该天天穿西装,就算自己以后简历没被选上,别人也会觉得,这家伙没衣服穿了,哈哈哈哈。
初试真的很小白。初试很突然,我也没准备,大概了解了一下,就算一些综合素养以及求职意愿的查看。我来列举一下hr问我的问题吧:(1)做一个自我介绍;(2)父母现在在哪里,家里有兄弟姐妹吗?(3)你在这个项目里面做了什么?(4)大概多久能够回复入职?(5)有没有向往这个方向走?(6)你还有什么问题吗?都是一些问你求职意愿的问题,但属实有些问题难到我了,就是第一个问题自我介绍,我感觉讲的有点啰嗦,最后直接被他打断了。但很遗憾我被老师通知复试了。
复试我是比较担心的,因为自己的确没怎么学好,简历上面有很多东西都是吹牛。
系统移植