C语言测试:想成为嵌入式程序员应知道的0×10个基本问题

本来是想搜索怎样测试C语言编写的嵌入式程序,碰巧找到了这篇文章,觉得不错,转载下,回头仔细看看。
查看原文

C语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为带面试者和被面试者提供许多有用信息,此外,撇开面试的压力不谈,这种测试也是相当有趣的。
从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?这个愚蠢的问题吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗?这标志着出题者也许花时间在微机上而不上在嵌入式系统上。如果上述任何问题的答案是“是”的话,那么我知道我得认真考虑我是否应该去做这份工作。
从面试者的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C语言的水平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择,还是只是瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我发现这些信息与他们的测试成绩一样有用。
有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给正在找工作的人一点帮住。这些问题都是我这些年实际碰到的。其中有些题很难,但它们应该都能给你一点启迪。
这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。 继续阅读 »

关于编辑器EditPlus

本文转自:http://zorro.blog.51cto.com/2139862/856553

引子
可以说文本编辑器是人们用得最多的软件之一,不管是家用还是办公,还是编程、作网页,虽然Windows本身的Notepad很简陋,但是有许多功能强大的共享编辑器软件让我们选择,比如UltraEdit、EditPlus、SynEdit、EditPad等,熟练掌握一款具有代表性的编辑器的详细用法,能够大大提高我们的工作效率。

EditPlus是非常有名的文本编辑器,它功能强大,特色丰富,中文兼容性好,是许多朋友必备的工具之一。下面,为大家详细介绍这个软件的使用。需要说明的是,本文试图以循序渐进的方式讲述。   对于许多应用程序,大部分操作使用键盘要快于鼠标,特别是文本编辑工具,更是如此。许多命令如果不用键盘操作就失去了存在的意义,因为用鼠标在选单里面一步步选取,相当不便。如果经常与编辑器打交道,掌握快捷键的操作很重要,可以节约很多时间。不必刻意去记忆,在使用过程里自然而然就会掌握。因为EditPlus的所有快捷键都可以由用户定义、修改,本文介绍各个命令时,以选单为主。 继续阅读 »

关于STM32使用RTC时复位后程序死在 RTC_WaitForSynchro() 函数中的问题

出现的现象:使用野火的RTC例程,在软件仿真时如果不需要配置,则程序会死在 RTC_WaitForSynchro() 函数中。而下载到硬件上时,有时候可以跑,有时候也会在该函数中死循环。

可能的原因:
首先,一定要确认是否使能了对后备寄存器和RTC的访问。
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操
作。执行以下操作将使能对后备寄存器和RTC的访问: 继续阅读 »

MSP430F2232使用ADC进行系统电池电压监测

ADC使用内部1.5V参考源,P2.0作为电池电压检测口。外部使用3M:1M分压电路。电压门限选定为2.45V和2.75V,对应的ADC的值为418和468.
ADC电压检测函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
u16 hal_ADC_measure(void)
{
//-----------开ADC电压测量-------------------------------------------------
// ,开启ADC10,允许中断,开启内部参考,V(REF+)=1.5v,V(R+) = V(REF+) and V(R-) = V(SS)
//  P2DIR &= ~BIT0;                         //P2.0 作为输入,要在开启ADC之前配置
//  delay_10us(100);
//  delay2ms();delay2ms();delay2ms();
ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + ADC10ON + ADC10IE; // ADC10ON, interrupt enabled
ADC10AE0 |= 0x01;                       // P2.0 模拟信号输入使能
ADC10CTL0 &= ~ENC;                    //ENC=0,修改A/D控制寄存器
ADC10CTL0 |= ENC + ADC10SC + ADC10ON;   //启动AD转换器
//延时,等待ADC转换完成
//      Set_TimeA1_ON(10);
//      __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit等待AD转换完成
//      Set_TimeA1_OFF();
delay_10us(30); //300us延时
//-----------------关闭ADC模块及IO处理---------------------------------------------------
ADC10CTL0 &= 0XFFFD;                    //ENC=0,修改A/D控制寄存器
ADC10CTL0 &= 0x0000;
ADC10AE0 &= 0xfe;                         // P2.0 模拟信号输入禁止
//  P2DIR |= BIT0;                            //P2.0 设置为输出
1
 
1
2
 return ADC10MEM;
}

 电压检测函数为:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void app_battary_voltage_detect(u16 CallTimes)
{
  static u8 TimeCnt = 0;        //检测时间计数
  static u8 VolThr275Cnt = 0;   //2.75v门限计数器,低于该值计数
  static u8 VolThr245Cnt = 0;   //2.45v门限计数器,低于该值计数

  u16 VoltageValue = 0;

  TimeCnt++;

//    if(TimeCnt >= CallTimes)
//    {
//      TimeCnt = 0;

    VoltageValue = hal_ADC_measure();   //读取电压值
#ifdef DEBUG_PRINTF
      hal_uart_printf("PowerDect: ",11);
      hal_uart_printu16(VoltageValue);
      hal_uart_printf("\r\n",2);
#endif

    if(VoltageValue <= V245_VREF_V15)     //电压低于2.45v
    {
      if (VolThr245Cnt < TIMES_CNT_BATTERY_LOW)
      {
        VolThr245Cnt++;
      }
      VolThr275Cnt = 0;
    }
    else if(VoltageValue <= V275_VREF_V15)//电压低于2.75v
    {
      if (VolThr275Cnt < TIMES_CNT_BATTERY_LOW)       {         VolThr275Cnt++;       }       VolThr245Cnt = 0;     }     else                                  //电压高于2.75v     {       VolThr245Cnt = 0;       VolThr275Cnt = 0;     }     if(VolThr245Cnt >= TIMES_CNT_BATTERY_LOW)       //连续5次低于2.45v,200s*5 = 1000s
    {
      g_uBatteryState = BATTERY_LOW;
    }
    else if (VolThr275Cnt >= TIMES_CNT_BATTERY_LOW)//连续5次低于2.75v
    {
      g_uBatteryState = BATTERY_WARNING;
    }
    else
    {
      g_uBatteryState = BATTERY_OK;
    }
//    }
}

MSP430使用TimerB定时进行传感器数据采集时遇到的定时错误

 问题现象:
在数据采集时一段时间会出现停止采集的情况,经过在线debug发现此时TBCCR0的值和TBR的值相差约为1.3s,即1.3s后才会再次产生TB0中断,才会再次采集数据。

使用背景:

用TB0做传感器定时采集,因为向传感器发送采集命令后要等待大约6ms才能获取到传感器数据,所以TB0的中断逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 __interrupt void Timer_B0_ISR(void)
 {
 TBCCTL0 &amp;= ~CCIE;  //关闭中断
 switch (SampleCtrl.State)
 {
 case SAMPLE_STATE_SEND:   //发送采集命令
 LED2ON;
 TBCCR0 = TBR + 262;   //8ms
 发送采集命令;
 SampleCtrl.State = SAMPLE_STATE_READ;
 break;
 case SAMPLE_STATE_READ:   //读取采集结果
 if (g_bDRDYFlg)   //DRDY脚中断使该标志置位,表明一次数据采集完成
 {
 g_bDRDYFlg = FALSE;
 SampleCtrl.State = SAMPLE_STATE_SEND;
 if  //采集的数据有效
 {
 EventReqQue |= DETECT_VEHICLE;
 //完成一次采集,设置剩余定时时间
 if (SAMPLE_FREQ_HI == SampleCtrl.Freq)  //设置为10ms-8ms
 {
 TBCCR0 = TBR + 65;   //2ms
 }
 else                                    //设置为100ms-32ms
 {
 TBCCR0 = TBR + 3014;   //92ms
 }
 LPM3_EXIT;
 }
 else  //采集的数据无效
 {
 TBCCR0 = TBR + 20;
 }
 }
 else  //数据采集尚未完成
 {
 TBCCR0 = TBR + 20;   //约60us后再次进入中断,再去读取数据。
 }
 break;
 default:
 SampleCtrl.State = SAMPLE_STATE_SEND;
 break;
 }
1
 
1
}

 

 问题原因:
由于在传感器数据采集时还需要跟24L01通信,为了保证SPI通信可靠,读写24L01时禁止了地磁采集,而禁止地磁采集时只是禁止了TB0中断,并没有停止TB,所以TB是一直在运行的,在TB0中断被禁止时TB0产生中断,则不会被响应,而开启TB0中断时人为清除了TB0中断标志,所以导致该次TB0中断不会被响应。从而TBCCR0不会被重新配置,使得TB0要在下一次等于TBCCR0时才能产生中断,而这个时间最大为2s。

解决方法:
在再次开启TB0中断时不应该人为清除TB0中断标志。因为这个中断是应该要响应的,只是在与24L01进行SPI通信时暂时被禁止。
一开始想要在禁止TB0中断时停止TB,这个思路是错误的,而且并没有针对问题的关键。一方面,TB是不应该被停止的,另一方面,导致2s左右没有采集地磁的真正原因是人为清除了TB0中断标志使得TBCCR0错过了一次配置。

ASCII字符5×7点阵

分享一份不错的ASCII字符5*7点阵。今天使用时遇到了一个非常郁闷的问题,显示小写字母时总是往后挪了一个字,花了好长时间,才找到原来是字符点阵中有一个注释 //\ ,因为 \ 在C语言中是续行符,所以导致把下一行点阵数据给注释掉了,哎,很是郁闷啊。

1
2
3
4
5
6
7
8
9
10
 // _ _ _ _ _
// 0|_|_|_|_|_|
// 1|_|_|_|_|_|
// 2|_|_|_|_|_|
// 3|_|_|_|_|_|
// 4|_|_|_|_|_|
// 5|_|_|_|_|_|
// 6|_|_|_|_|_|
// 7|_|_|_|_|_| BLANK
// 0 1 2 3 4

0×00,0×00,0×00,0×00,0×00, // SP
0×00,0×00,0x4F,0×00,0×00, // !
0×00,0×07,0×00,0×07,0×00, // ”
0×14,0x7F,0×14,0x7F,0×14, // #
0×24,0x2A,0x7F,0x2A,0×12, // $
0×23,0×13,0×08,0×64,0×62, // %
0×36,0×49,0×55,0×22,0×50, // &
0×00,0×05,0×03,0×00,0×00, // ‘
0×00,0x1C,0×22,0×41,0×00, // (
0×00,0×41,0×22,0x1C,0×00, // )
0×14,0×08,0x3E,0×08,0×14, // *
0×08,0×08,0x3E,0×08,0×08, // +
0×00,0×50,0×30,0×00,0×00, // ,
0×08,0×08,0×08,0×08,0×08, // -
0×00,0×60,0×60,0×00,0×00, // .
0×20,0×10,0×08,0×04,0×02, // /

0x3E,0×51,0×49,0×45,0x3E, // 0
0×00,0×42,0x7F,0×40,0×00, // 1
0×42,0×61,0×51,0×49,0×46, // 2
0×21,0×41,0×45,0x4B,0×31, // 3
0×18,0×14,0×12,0x7F,0×10, // 4
0×27,0×45,0×45,0×45,0×39, // 5
0x3C,0x4A,0×49,0×49,0×30, // 6
0×01,0×71,0×09,0×05,0×03, // 7
0×36,0×49,0×49,0×49,0×36, // 8
0×06,0×49,0×49,0×29,0x1E, // 9

0×00,0×36,0×36,0×00,0×00, // :
0×00,0×56,0×36,0×00,0×00, // ;
0×08,0×14,0×22,0×41,0×00, // < 0×14,0×14,0×14,0×14,0×14, // = 0×41,0×22,0×14,0×08,0×00, // >
0×02,0×01,0×51,0×09,0×06, // ?

0×32,0×49,0×79,0×41,0x3E, //@
0x7E,0×11,0×11,0×11,0x7E, //A
0x7F,0×49,0×49,0×49,0×36, //B
0x3E,0×41,0×41,0×41,0×22, //C
0x7F,0×41,0×41,0×22,0x1C, //D
0x7F,0×49,0×49,0×49,0×41, //E
0x7F,0×09,0×09,0×09,0×01, //F
0x3E,0×41,0×49,0×49,0x7A, //G
0x7F,0×08,0×08,0×08,0x7F, //H
0×00,0×41,0x7F,0×41,0×00, //I
0×20,0×40,0×41,0x3F,0×01, //J
0x7F,0×08,0×14,0×22,0×41, //K
0x7F,0×40,0×40,0×40,0×40, //L
0x7F,0×02,0x0C,0×02,0x7F, //M
0x7F,0×04,0×08,0×10,0x7F, //N
0x3E,0×41,0×41,0×41,0x3E, //O

0x7F,0×09,0×09,0×09,0×06, //P
0x3E,0×41,0×51,0×21,0x5E, //Q
0x7F,0×09,0×19,0×29,0×46, //R
0×46,0×49,0×49,0×49,0×31, //S
0×01,0×01,0x7F,0×01,0×01, //T
0x3F,0×40,0×40,0×40,0x3F, //U
0x1F,0×20,0×40,0×20,0x1F, //V
0x3F,0×40,0×38,0×40,0x3F, //W
0×63,0×14,0×08,0×14,0×63, //X
0×03,0×04,0×78,0×04,0×03, //Y
0×61,0×51,0×49,0×45,0×43, //Z

0×00,0x7F,0×41,0×41,0×00, //[
0x02,0x04,0x08,0x10,0x20, /*\*/
0x00,0x41,0x41,0x7F,0x00, //]
0×04,0×02,0×01,0×02,0×04, //^
0×40,0×40,0×40,0×40,0×40, //_

0×00,0×01,0×02,0×04,0×00, //`
0×20,0×54,0×54,0×54,0×78, //a
0x7F,0×48,0×44,0×44,0×38, //b
0×38,0×44,0×44,0×44,0×20, //c
0×38,0×44,0×44,0×48,0x7F, //d
0×38,0×54,0×54,0×54,0×18, //e
0×08,0x7E,0×09,0×01,0×02, //f
0×08,0×54,0×54,0×54,0x3C, //g
0x7F,0×08,0×04,0×04,0×78, //h
0×00,0×48,0x7D,0×40,0×00, //i
0×20,0×40,0×44,0x3D,0×00, //j
0x7F,0×10,0×28,0×44,0×00, //k
0×00,0×41,0x7F,0×40,0×00, //l
0x7C,0×04,0×78,0×04,0×78, //m
0x7C,0×08,0×04,0×04,0×78, //n
0×38,0×44,0×44,0×44,0×38, //o

0x7C,0×14,0×14,0×14,0×08, //p
0×08,0×14,0×14,0×18,0x7C, //q
0x7C,0×08,0×04,0×04,0×08, //r
0×48,0×54,0×54,0×54,0×20, //s
0×04,0x3F,0×44,0×40,0×20, //t
0x3C,0×40,0×40,0×20,0x7C, //u
0x1C,0×20,0×40,0×20,0x1C, //v
0x3C,0×40,0×30,0×40,0x3C, //w
0×44,0×28,0×10,0×28,0×44, //x
0x0C,0×50,0×50,0×50,0x3C, //y
0×44,0×64,0×54,0x4C,0×44, //z

0×00,0×08,0×36,0×41,0×00, //{
0×00,0×00,0x7F,0×00,0×00, //|
0×00,0×41,0×36,0×08,0×00, //}
0×10,0×08,0×08,0×10,0×08 //~

最新百度网盘邀请链接免费分享

百度也出网盘了,呵呵,云计算这块大蛋糕,各位大佬都不会放的,分享几个邀请链接给大家。

http://pan.baidu.com/netdisk/beinvited?invite_code=d46af05bf71b5600928fd3066a96046b

http://pan.baidu.com/netdisk/beinvited?invite_code=54663733a8de47dc0c56ca64dcb83484

http://pan.baidu.com/netdisk/beinvited?invite_code=8913af14d3d8b7205a3244b0b222ea68

http://pan.baidu.com/netdisk/beinvited?invite_code=93bd1c67beaef5387876adc7be43119e

http://pan.baidu.com/netdisk/beinvited?invite_code=b9ffcfb597576cc720591d903343c9ec

http://pan.baidu.com/netdisk/beinvited?invite_code=ee3a874e158108fa3056e08d06fc1e45

http://pan.baidu.com/netdisk/beinvited?invite_code=fb1259d8c1f8bcb944c8b1d5ea5faa15

http://pan.baidu.com/netdisk/beinvited?invite_code=78520c453903754a8a9fbbecd1206b88

http://pan.baidu.com/netdisk/beinvited?invite_code=094c18e311c2eef0f6f014d937182e31

http://pan.baidu.com/netdisk/beinvited?invite_code=c73fa995551c14d2518cd92a8bb29659

开漏输出与推挽输出

开漏输出:MOS管漏极开路。
要得到高电平需要上拉电阻才行。一般用于线或、线与,适合做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。开漏是对MOS管而言,开集是对双极型管而言,在用法上没区别,开漏输出端相对于三极管的集电极。如果开漏引脚不连接外部的上拉电阻,则只能输出低电平。因此,对于经典的51单片机的P0口,要想做输入/输出功能必须加外部上拉电阻,否则无法输出高电平逻辑。一般来说,可以利用上拉电阻接不通的电压,改变传输电平,以连接不通电平(3.3V或5V)的器件或系统,这样就可以进行任意电平转换了。
漏极开路输出高电平本质就相当于IO口断开,直接被上拉电阻上拉;而输出低电平实际就是电流被IO口吸收。
推挽输出:
如果输出级的两个参数相同MOS管(或三极管)受两互补信号的控制,始终处于一个导通,一个截止状态,就是推挽项链,这种结构称为推拉式电路。推挽输出电路输出高电平或低电平时,两个MOS管交替工作,可以减低功耗,并提高每个管的承受能力。又由于不论走那条路,管制导通电阻都很小,使RC常数很小,逻辑电平转变速度很快,因此,推拉式输出既可以提高电路的负载能力,又能提高开关速度,且导通损耗小效率高。输出既可以向负载灌电流(作为输出),也可以从负载抽取电流(作为输入)。

晶振和晶振电容

如何选择晶振
对于一个高可靠性系统,晶振的选择非常重要,尤其是设计带有睡眠功唤醒(往往用低电压以求低功耗)的系统。这是因为低供电电压是提供给晶振的激励公路减少,造成晶振起振很慢或根本就不起振。这一现象在上电复位时并不特别明显,原因是上电时电路有足够的扰动,很容易建立正当。在睡眠唤醒时,电路的扰动要比上电时小得多,起振变得很不容易。
在振荡回路中,晶振既不能过激励(容易振到高次谐波上),也不能前激励(不容易起振)。
晶振的选择至少必须考虑谐振点、负载电容、激励功率、温度特性、长期稳定性。

如何选择晶振电容
(1)因为每种晶振都有各自特性,所以最好按芯片制造厂商提供的数值选择外部器件。
(2)电容值小,容易起振;但过低,振荡器容易不稳定。电容值大,有利于振荡器的稳定,但过大,会增加起振时间,不容易起振。
以上内容摘自《基于ARM Cortex-M3的STM32系列嵌入式微控器应用实践》

STM32上电默认是使用内部高速RC时钟(HIS),因此,用示波器检查OSC引脚是否有时钟信号来判断STM32单片机最小系统是否工作是错误的。

STM32单片机RTC使用的低速晶振要求比较苛刻,需要使用负载电容为6pF的晶振,而最常见的32.768kHz晶振负载电容为12.5pF。ST数据手册上特别强调了这一点。据说使用常规晶振有10%的死机可能。

STM32F103系列命名规则

对于STM32F103xxyy系列:
第一个x代表引脚数:T-36pin,C-48pin,R-64pin,V-100pin,Z-144pin;
第二个x代表Flash容量:6-32K,8-64K,B-128K,C-256K,D-384K,E-512K;
第一个y代表封装:H-BGA封装,T-LQFP封装,U-QFN封装;
第二个一代表工作稳定范围:6代表-40到85摄氏度,7代表-40到105摄氏度。

STM32笔记之FatFS文件系统移植

从官网下载到最新的文件系统FatFS版本 Sep 06,’11 R0.09

需要修改的文件是 ffconf.h 和 diskio.c(新版本不提供该文件,连模板都没有提供,完全需要自己编写)。 ffconf.h是文件系统配置文件,主要是配置文件系统的各种功能。  diskio.c是底层驱动,从diskio.h中知道diskio.c应该包含如下函数:

1
2
3
4
5
6
7
8
int assign_drives (int, int);
 DSTATUS disk_initialize (BYTE);
 DSTATUS disk_status (BYTE);
 DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
 #if  _READONLY == 0
 DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
 #endif
 DRESULT disk_ioctl (BYTE, BYTE, void*);

ffconf.h文件中我修改了如下几个地方:

 

1
2
#define _USE_STRFUNC  1 /* 0:Disable or 1-2:Enable */
 /* To enable string functions, set _USE_STRFUNC to 1 or 2. */
1
2
3
#define _CODE_PAGE  850
 /* The _CODE_PAGE specifies the OEM code page to be used on the target system.
 /  Incorrect setting of the code page can cause a file open failure.
1
2
3
#define _USE_LFN  1   /* 0 to 3 */
 #define  _MAX_LFN  255   /* Maximum LFN length to handle (12 to 255) */
 /* The _USE_LFN option switches the LFN support.

遇到过的问题:
 1._CODE_PAGE 配置错误导致文件打开失败。根据文件里的注释,中文应该选择_CODE_PAGE=936,同时应该添加cc936.c文件。我的应用中只需要支持英文长文件名就可以了。所以使用 850 就可以,同时添加ccsbcs.c文件。

1
2.长文件名支持:第93行,#define &nbsp; &nbsp;_USE_LFN &nbsp; &nbsp;1,支持长文件名。

STM32 RTC更新时间计数器RTC_CNT后在函数RTC_WaitForLastTask()中死循环的原因

出现的现象是上电初始化时配置RTC没有错误,程序顺利通过,但是在程序运行过程中校正时间,更新RTC_CNTx的值,程序就会停在RTC_WaitForLastTask()进行死循环。

问题的原因就在于,每次操作RTC_CNTx时应该要使能PWR 和 BKP 时钟,允许访问BKP域。

例程中只在第一次上电,并且初始化RTC后才执行了写RTC_CNTx的操作,如下:

1
2
3
4
5
6
7
printf("\r\n\n RTC not yet configured....");
/* RTC Configuration */
RTC_Configuration();
printf("\r\n RTC configured....");

/* Adjust time by values entered by the user on the hyperterminal */
Time_Adjust();

而在校正时间,更新RTC_CNTx时不能直接调用Time_Adjust()函数,需要开启PWR 和 BKP 时钟,且允许访问BKP域

1
2
3
4
5
6
7
8
9
10
11
/* Enable PWR and BKP clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* Allow access to BKP Domain */
PWR_BackupAccessCmd(ENABLE);

/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* 修改当前RTC计数寄存器内容 */
RTC_SetCounter(Cnt);
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();

其实如果能仔细看看手册应该不会出现这样的错误。不过幸亏有网络,有前人的经验,我没有话太多时间。

为什么用Keil编译的hex文件这么大

刚刚接触STM32的开发,发现用RVMDK编译生成的hex文件比用EWARM生成的大很多,同一个工程,前者生成的文件有31K,后者只有6K。原因在哪里?

IAR编译时,没有调用的函数是不会编译到hex文件中的,Keil是不是把没有调用的函数编译到hex文件中去了呢?真实情况就是这样。

作为这么一款优秀的开发工具,Keil不会这么白痴吧。

实际上是,Keil提供一个选项,由用户决定是否把没有调用的函数编译到目标文件中去。

设置方法如下:

Options ->  C/C++ -> Language/Code Generation  勾选 “One ELF Section per Function”

试试看吧。

感谢同事,呵呵。

为什么用 (year) % 4 == 0 就可以判断是否是闰年

第一眼看到这样判断闰年觉得很奇怪,明明闰年的判断条件是:能被4整除且不能被100整除;或者能被400整除。这里为什么可以用能被4整除来判断呢?

其实这条语句成立是有条件的,只在一定范围内存在,即只在100年范围内存在,如2001年到2099年范围。很明显,这个范围内的数都不能被100整除。

虽然很多单片机系统使用这种方法判断闰年,因为工程师认为自己的产品能用10年就不错了,%>_<%

但我还是倾向于用以下代码来判断,虽然效率会稍稍第一点。

1
2
3
 if ((nyear % 4 == 0 &amp;&amp; nyear % 100 != 0 )|| (nyear % 400 == 0))
return TRUE;
return FALSE;

FatFS文件系统中get_fattime()函数的实现

1
2
3
4
5
6
 bit31:25 年 (0~127)(从1980 开始)
bit24:21 月 (1~12
bit20:16 日 (1~31
bit15:11 小时(0~23
bit10:5 分钟(0~59
bit4:0 秒 (0~29

函数实现:

实现1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 DWORD get_fattime (void)
{
DWORD RetVal = 0;
DWORD Temp = 0;

Temp = g_SysTime.tm_year - 1980;
RetVal = (Temp &lt;&lt; 25);
Temp = g_SysTime.tm_mon;
RetVal |= (Temp &lt;&lt; 21);
Temp = g_SysTime.tm_mday;
RetVal |= (Temp &lt;&lt; 16);
Temp = g_SysTime.tm_hour;
RetVal |= (Temp &lt;&lt; 11);
Temp = g_SysTime.tm_min;
RetVal |= (Temp &lt;&lt; 5);
Temp = g_SysTime.tm_sec&gt;&gt;2;
RetVal |= (Temp);

return RetVal;
}

实现2:

1
2
3
4
5
6
7
8
9
10
 DWORD get_fattime (void)
{
return ((2010UL-1980) &lt;&lt; 25) /* Year = 2010 */
| (11UL &lt;&lt; 21) /* Month = 11 */
| (2UL &lt;&lt; 16) /* Day = 2 */
| (15U &lt;&lt; 11) /* Hour = 15 */
| (0U &lt;&lt; 5) /* Min = 0 */
| (0U &gt;&gt; 1) /* Sec = 0 */
;
}
第 1 页,共 22 页12345...1020...最旧 »

无觅相关文章插件,快速提升流量