fish001

  • 2019-09-14
  • 发表了主题帖: 有关MSP430 使用一些理解

    1.中断嵌套,优先级  430总中断的控制位是状态寄存器内的GIE位(该位在SR寄存器内),该位在复位状态下,所有的可屏蔽中断都不会发生响应。可屏蔽中断又分为单中断源和多中断源的。单中断源的一般响应了中断服务程序中断标志位就自动清零,而多中断源的则要求查询某个寄存器后中断标志位才会清零。由于大多数人接触的第一款单片机通常是51,51单片机CPU在响应低优先级的中断程序过程中若有更高优先级的中断发生,单片机就会去执行高优先级,这个过程已经产生了中断嵌套。而430单片机则不同,如果在响应低优先级中断服务程序的时候,即使来了更高优先级的中断服务请求,430也会置之不理,直至低优先级中断服务程序执行完毕,才会去响应高优先级中断。这是因为430在响应中断程序的时候,总中断GIE是复位状态的,如果要产生类似51的中断嵌套,只能在中断函数内再次置位GIE位。  2.定时器TA  TimerA有2个中断向量。TIMERA0,TIMERA1  TIMERA0只针对CCR0的计数溢出  TIMERA1再查询TAIV后可知道是CCR1,还是CCR2,亦或TAIFG引起的,至于TAIFG是什么情况下置位的,则要看TA工作的模式  具体看用户手册。还有一点TA本身有PWM输出功能,无须借用中断功能。在这个问题上经常出现应用弯路的是如何结合TA和AD实行定时采样的问题,很多人都是在TA中断里打开AD这样来做。这是不适宜的,因为430 的ADC10,ADC12(SD16不熟悉,没发言权)模块均有脉冲采样模式和扩展采样模式。只要选择AD是由TA触发采样,然后把TA设置成PWM输出模式,当然输出PWM波的都是特殊功能脚,但是在这里它是不需要输出的,所以引脚设置不必理会。值得关心的就是PWM的频率,也就是你AD的采样率。  3.看门狗复位  看门狗有2种工作模式:定时器 ,看门狗  定时器工作模式下WDTIFG在响应中断服务程序有标志位自动复位,而在看门狗模式下,该标志位只能软件清零。但是怎么判断复位是由于WDT工作在看门狗模式下的定时溢出引起的,还是看门狗写密钥错误引起的呢?………………………………  答案是没有方法,至少我没见过有什么方法,也没见过周边的人有什么方法。若有人知道方法谢谢分享。  4.经常有人会问这个语句的MOV.B  #LPM0,0(SP)的作用。假如你在进入中断函数之前,430是在LPM0下待机,若要求执行完中断函数之后进入LPM3待机,在中断函数里写MOV.B  #LPM3,SR是无效的。因为在进入中断时430会把PC,SR压栈,( SR内保存着低功耗模式的设置)即使你写了MOV.B  #LPM3,SR,在退出中断出栈时SR会被重新设置成低功耗0,要达到这样的目的,只能更改堆栈内SR的设置:MOV.B  #LPM0,0(SP)。  5中断向量:  430的中断向量是FFE0H—FFFFH,一共32个字节也就是FLASH的最后一段,430的FLASH有大有小,但是最后地址肯定是FFFFH(大FLASH超过64K的除外)所以它们的起始地址是不一样的,而一般IAR默认编译都是把程序放在FLASH开始的位置(不包括信息段)。  有个值得弄清楚的问题是:什么是中断向量?中断向量实际就是保存中断函数入口地址的存储单元空间。就像FFFEH+FFFFH这2个字节是复位中断向量,那么它存储的就是主函数在FLASH内的起始地址,假如主函数保存在以0x1100为起始地址的FLASH块内 ,那么你会发现FFFFH 内保存的是0x11, FFFE内保存的是0x00.其他什么TimerA,ADC12,所有的都一样。只是你每次写的程序长短不一,中断函数放的位置不一样。IAR编译器都会给你定好,然后在你用JTAG烧写程序的时候,把这个地址,烧写到相应的中断向量。因为中断函数所处地址可以由用户自定义,也可以让IAR自动编译,所以这个地址除了源代码开发人员知道,其他人是不知道的,BSL就是应用这32个字节的中断向量内的内容的特殊性设置的密码。但是有几个东西在430是不变的,就是触发中断的条件满足后,它到哪个地方去寻址中断服务函数的入口地址,是TI 在做430时就固化好,定死的。比方说上电复位的时候,它知道去FFFE,FFFF单元找地址,而不去FFE0,FFE2找地址,这个映射关系是430固化不变的。可有的时候你就是需要改变“中断向量”,这怎么办?430FLASH程序自升级里有时就会碰到这个问题,方法是在430原来默认的中断向量表内做一个跳转操作,同样以上电复位为例:  ORG  0x2345  PowerReset: mov.w  &0xFCFE,PC  ORG  0xFFFE  DW   PowerReset  这样的话0xFCFE就相当是0xFFFE的映射了。这个在430程序自升级的TI应用报告里就有。  有点晚,就到这里。有新的内容的话,我会添加。希望大家能够喜欢。

  • 发表了主题帖: MSP430晶振配置详解

    MSP430(F5529)相比MSP430(F149)来讲,功能更加强大。 UCS简介 MSP430F5XX/MSP430F6XX系列器件的UCS包含有五种时钟源,依次是:XT1CLK、VLOCLK、REFOCLK、DCOCLK和XT2CLK。这五种时钟的详细介绍请参考该系列芯片的指导手册,其中XT1CLK、VLOCLK、REFOCLK和XT2CLK跟MSP430F1XX系列没有太大区别,学习配置起来也比较简单。 UCS上电默认状态 PUC后,UCS模块的默认状态如下: (1)XT1处于LF模式作为XT1CLK时钟源。ACLK选通为XT1CLK。   (2)MCLK选通为DCOCLKDIV   (3)SMCLK选通为DCOCLKDIV   (4)FLL使能,且将XT1CLK作为FLL参考时钟。   (5)XIN和XOUT脚设置为通用IO,XIN和XOUT配置为XT1功能前,XT1保持禁用。   (6)如果可用的话,XT2IN和XT2OUT被设置为通用IO且保持禁止状态。   清楚UCS上电默认状态是非常重要的,这对于理解后面的配置逻辑来说非常重要。 UCS时钟源切换 由于REFOCLK、VLOCLK、DCOCLK(这里暂时这么认为)默认状态下是可用的,所以,切换的时候只需要通过UCSCTL4来配置ACLK、SMCLK和MCLK的时钟源即可,而XT1CLK和XT2CLK需要根据硬件的具体配置情况确定,所以,这两者的配置比起前三者来讲,就有些不同了。下面,我们做三个实验: (1)将MCLK和SMCLK配置REFOCLK、VLOCLK REFOCLK和VLOCLK是芯片默认提供的,只要芯片正常工作,这两个时钟就会正常工作,因此,该时钟配置非常简单,只需要修改UCSCTL4,将SELS和SELM配置为对应的选项VLOCLK或者REFOCLK即可,具体代码如下: #include <msp430f5529.h>         void main(void) {     WDTCTL = WDTPW+WDTHOLD;          P1SEL |= BIT0;     P1DIR |= BIT0;//测量ACLK用     P2SEL |= BIT2;     P2DIR |= BIT2;//测量SMCLK用     P7SEL |= BIT7;     P7DIR |= BIT7;//测量MCLK用           //UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_1|SELM_1; //将SMCLK和MCLK配置为VLOCLK     UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_2|SELM_2; //将SMCLK和MCLK配置为REFOCLK           while(1);   }   上面的代码就实现了将SMCLK和MCLK切换为VLOCLK和REFOCLK,ACLK的操作也是同样的,不作过多解释。 (2)将MCLK和SMCLK配置XT1CLK   我手头上的开发板XT1外接的是32.768K的手表时钟晶振,XT1CLK的配置要分为以下几步: 1.配置IO口5.4和5.5为XT1功能。   2.配置XCAP为XCAP_3,即12PF的电容。   3.清除XT1OFF标志位。   4.等待XT1起振。   具体的代码如下: #include <msp430f5529.h>      void main(void) {     WDTCTL = WDTPW+WDTHOLD;          P1SEL |= BIT0;     P1DIR |= BIT0;//测量ACLK用     P2SEL |= BIT2;     P2DIR |= BIT2;//测量SMCLK用     P7SEL |= BIT7;     P7DIR |= BIT7;//测量MCLK用        P5SEL |= BIT4|BIT5; //将IO配置为XT1功能     UCSCTL6 |= XCAP_3;  //配置电容为12pF     UCSCTL6 &= ~XT1OFF; //使能XT1        while (SFRIFG1 & OFIFG){       UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);         // 清除三类时钟标志位                                 // 这里需要清除三种标志位,因为任何一种                                 // 标志位都会将OFIFG置位       SFRIFG1 &= ~OFIFG;                                  // 清除时钟错误标志位     }     UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_0|SELM_0;     //将SMCLK和MCLK时钟源配置为XT1     while(1);   }   (3)将SMCLK和MCLK配置XT2 将SMCLK和MCLK配置为XT2跟配置为XT1的过程基本相同,唯一不同的是,在配置SMCLK和MCLK为XT2之前,需要将ACLK和REFCLK的时钟源,因为ACLK和REFCLK的默认时钟源是XT1,而我们这里并没有配置启动XT1CLK,所以会产生XT1时钟错误,即XT1LFFG,因此,我们先将ACLK和REFCLK配置为芯片自带的时钟(REFOCLK或VLOCLK)或者即将启动的时钟(XT2),此外,XT2配置时不需要配置电容,故将SMCLK和MCLK配置为XT2的代码如下: #include <msp430f5529.h>      void main(void) {     WDTCTL = WDTPW+WDTHOLD;          P1SEL |= BIT0;     P1DIR |= BIT0;//测量ACLK用     P2SEL |= BIT2;     P2DIR |= BIT2;//测量SMCLK用     P7SEL |= BIT7;     P7DIR |= BIT7;//测量MCLK用        P5SEL |= BIT2|BIT3; //将IO配置为XT2功能     UCSCTL6 &= ~XT2OFF; //使能XT2        UCSCTL4 = UCSCTL4&(~(SELA_7))|SELA_1; //先将ACLK配置为VLOCLK     UCSCTL3 |= SELREF_2;                  //将REFCLK配置为REFCLK        while (SFRIFG1 & OFIFG){       UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);         // 清除三类时钟标志位                                 // 这里需要清除三种标志位,因为任何一种                                 // 标志位都会将OFIFG置位       SFRIFG1 &= ~OFIFG;                                  // 清除时钟错误标志位     }     UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_5|SELM_5;     //将SMCLK和MCLK时钟源配置为XT2     while(1);   }   做完前面三个实验,我们就能掌握MSP430F5XX系列时钟切换的基本操作了,讲的并不详细,有其他疑问请仔细阅读芯片手册或者留言讨论。 DCO模块详解 DCO模块在MSP430F5XX系列芯片中非常重要,因为从MSP430F4XX开始,MSP430引用了FLL模块,FLL即锁相环,可以通过倍频的方式提高系统时钟频率,进而提高系统的运行速度。 DCO模块运行需要参考时钟REFCLK,REFCLK可以来自REFOCLK、XT1CLK和XT2CLK,通过UCSCTL3的SELREF选择,默认使用的XT1CLK,但如果XT1CLK不可用则使用REFOCLK。 DCO模块有两个输出时钟信号,级DCOCLK和DCOCLKDIV,其中,倍频计算公式如下: DCOCLK = D*(N+1)*(REFCLK/n)   DCOCLKDIV = (N+1)*(REFCLK/n)   其中: n即REFCLK输入时钟分频,可以通过UCSCTL3中的FLLCLKDIV设定,默认为0,也就是不分频; D可以通过UCSCTL2中的FLLD来设定,默认为1,也就是2分频; N可以通过UCSCTL2中的FLLN来设定,默认值为32。 所以,系统上电后如果不做任何设置,DCOCLK的实际值为2097152,DCOCLKDIV的实际值为1048576。 另外,配置芯片工作频率还需要配置DCORSEL和DCOx,DCORSEL和DCOx的具体作用如下: DCORSEL位于UCSCTL1控制寄存器中的4到6位,共3位,将DCO分为8个频率段。 DCOx位于UCSCTL0中的8到12位,共5位,将DCORSEL选择的频率段分为32个频率阶,每阶比前一阶高出约8%,该寄存器系统可以自动调整,通常配置为0。 DCORSEL和DCOx值的具体作用可以参考MSP430F5529的数据手册,阅读该手册相关部分可以找到如下表格:   可以见,DCORESL的频率调节范围大致如下: DCORSEL = 0的调节范围约为0.20~0.70MHZ;   DCORSEL= 1的调节范围约为0.36~1.47MHZ;   DCORSEL = 2的调节范围约为0.75~3.17MHZ;   DCORSEL = 3的调节范围约为1.51~6.07MHZ;   DCORSEL = 4的调节范围约为3.2~12.3MHZ;   DCORSEL = 5的调节范围约为6.0~23.7MHZ;   DCORSEL = 6的调节范围约为10.7~39.7MHZ;   DCORSEL = 7的调节范围约为19.6~60MHZ。   理解了上面这些,可以理解TI官方例子中的代码了,官方代码中的相关部分如下: if (fsystem <= 630)            //           fsystem < 0.63MHz     UCSCTL1 = DCORSEL_0;   else if (fsystem <  1250)      // 0.63MHz < fsystem < 1.25MHz     UCSCTL1 = DCORSEL_1;   else if (fsystem <  2500)      // 1.25MHz < fsystem <  2.5MHz     UCSCTL1 = DCORSEL_2;   else if (fsystem <  5000)      // 2.5MHz  < fsystem <    5MHz     UCSCTL1 = DCORSEL_3;   else if (fsystem <  10000)     // 5MHz    < fsystem <   10MHz     UCSCTL1 = DCORSEL_4;   else if (fsystem <  20000)     // 10MHz   < fsystem <   20MHz     UCSCTL1 = DCORSEL_5;   else if (fsystem <  40000)     // 20MHz   < fsystem <   40MHz     UCSCTL1 = DCORSEL_6;   else     UCSCTL1 = DCORSEL_7;   都在前面讲到的范围内,由于前面的范围有重叠部分,例子代码中的值是TI的工程师根据上面这些参数选取的比较合理的值。 到这里,我相信大家配合芯片手册和本文,都能明白DCO配置相关部分的原理了,下面是将DCO参考时钟选为XT1,并将DCOCLK倍频到25M的详细代码: #include <msp430f5529.h>      void delay(){     volatile unsigned int i;     for(i = 0; i != 5000; ++i){       _NOP();     }   }      void SetVcoreUp (unsigned int level)   {     // Open PMM registers for write     PMMCTL0_H = PMMPW_H;     // Set SVS/SVM high side new level     SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;     // Set SVM low side to new level     SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;     // Wait till SVM is settled     while ((PMMIFG & SVSMLDLYIFG) == 0);     // Clear already set flags     PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);     // Set VCore to new level     PMMCTL0_L = PMMCOREV0 * level;     // Wait till new level reached     if ((PMMIFG & SVMLIFG))       while ((PMMIFG & SVMLVLRIFG) == 0);     // Set SVS/SVM low side to new level     SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;     // Lock PMM registers for write access     PMMCTL0_H = 0x00;   }      void main(void) {     WDTCTL = WDTPW+WDTHOLD;     P1SEL &= ~BIT1;     P1DIR |= BIT1;          P1SEL |= BIT0; //ACLK     P1DIR |= BIT0;     P2SEL |= BIT2; //SMCLK     P2DIR |= BIT2;     P7SEL |= BIT7; //MCLK     P7DIR |= BIT7;        P5SEL |= BIT4|BIT5;     UCSCTL6 |= XCAP_3;     UCSCTL6 &= ~XT1OFF;        SetVcoreUp(1); //一次提高Vcore电压等级,具体请参考手册     SetVcoreUp(2);     SetVcoreUp(3);        __bis_SR_register(SCG0);     UCSCTL0 = 0;     UCSCTL1 = DCORSEL_6;     UCSCTL2 = FLLD_1 | 380;     __bic_SR_register(SCG0);     __delay_cycles(782000);        /*     * 默认状态下:ACLK=FLLREFCLK=XT1 SMCLK=MCLK=DCOCLKDIV XT2关闭     * 为了不产生XT1LFOFFG,将ACLK和FLLREFCLK设置为REFOCLK     * 并打开XT2OFF,否则XT2将处于无法使用状态     * */     //UCSCTL6 &= ~(XT2DRIVE0|XT2DRIVE1|XT2OFF);        while (SFRIFG1 & OFIFG) {                               // Check OFIFG fault flag       UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);         // Clear OSC flaut Flags       SFRIFG1 &= ~OFIFG;                                  // Clear OFIFG fault flag     }        UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_3|SELM_3;        while(1){       P1OUT ^= BIT1;       delay();     }   }  

  • 发表了主题帖: msp430f5529 uart pwm adc

    //msp430f5529的串口使用程序使用示例 //--------------------------------------------- uart 头文件 -------------------------------------------------------------// #ifndef UART_H_ #define UART_H_ #include <msp430f5529.h> #include "config.h" //默认为115200   void USCIA0_Init(void); void USCIA0_SendChar(u8 c); u8 USCIA0_ReceiveChar(void); void USCIA0_SendString(u8 *str); void USCIA1_Init(void); void USCIA1_SendChar(u8 c); u8 USCIA1_ReceiveChar(void); void USCIA1_SendString(u8 *str); //void Uart1_Send_AF(void); #endif /* #ifndef USCI_A0_h */ /* //使用例程 void main(void) {     volatile unsigned int i;     u8 data;        dog_Disable();     UCS_Init();     //key_Init();     USCIA0_Init();        while(1)     {         USCIA0_SendChar('A');         USCIA0_SendString(" luoxn28");         data = USCIA0_ReceiveChar(); if(data != 0) {         USCIA0_SendChar(data);         USCIA0_SendChar('\r');USCIA0_SendChar('\n'); }               delay(10);           } } */ //--------------------------------------------- uart 源文件 -------------------------------------------------------------// #include "msp430_UART.h" #if USCIA0_EN >0 /********************************************************* *名称:USCIA0_Init *功能:串口初始化 *入口参数:无 *出口参数:无 *说明:设置为P3.3和P3.4为串口通信端口 3.3-UCA0TXD 3.4-UCA0RXD **********************************************************/ void USCIA0_Init(void) {   P3SEL |= BIT3+BIT4;                       // P3.3,4 = USCI_A0 TXD/RXD   UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**   UCA0CTL1 |= UCSSEL__ACLK;                     // ACLK   UCA0BR0 = 34;           // 4MHz 115200   UCA0BR1 = 0;            // 4MHz 115200   UCA0MCTL |= UCBRS_6 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0   UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**   //IE2 |= UCA0RXIE + UCA0TXIE;  // Enable USCI_A0 TX/RX interrupt   //IE2 |= UCA0RXIE;             // Enable USCI_A0 RX interrupt   //__bis_SR_register(GIE);      // Enter LPM3 w/ interrupts enabled } //串口0发送字符函数 void USCIA0_SendChar(u8 c) {     UCA0TXBUF=c;        while(!(UCA0IFG & UCTXIFG));   // USCI_A0 TX buffer ready?     UCA0IFG &= ~UCTXIFG; } //串口0接收字符函数 u8 USCIA0_ReceiveChar(void) {     u8 data = 0; //阻塞式返回值     while(!(UCA0IFG & UCRXIFG));   // USCI_A0 TX buffer ready?     UCA0IFG &= ~UCRXIFG;     data = UCA0RXBUF;     return data; /*  //非阻塞式返回值     if(UCA0IFG & UCRXIFG)   { UCA0IFG &= ~UCRXIFG; data = UCA0RXBUF; } return data; */     } //串口0发送字符串函数 void USCIA0_SendString(u8 *str) {     while(*str != '\0')     {         USCIA0_SendChar(*str);         str++;     } } #endif //#if USCIA0_EN >0 #if USCIA1_EN > 0 /********************************************************* *名称:USCIA0_Init *功能:串口初始化 *入口参数:无 *出口参数:无 *说明:设置为P4.4和P4.5为串口通信端口 4.4-UCA1TXD 4.5-UCA1RXD **********************************************************/ //串口1初始化函数 void USCIA1_Init(void) {   P4SEL |= BIT4+BIT5;                       // P3.3,4 = USCI_A0 TXD/RXD   UCA1CTL1 |= UCSWRST;                      // **Put state machine in reset**   UCA1CTL1 |= UCSSEL__ACLK;                     // ACLK   UCA1BR0 = 34;           // 4MHz 115200   UCA1BR1 = 0;            // 4MHz 115200   UCA1MCTL |= UCBRS_6 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0   UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**   //IE2 |= UCA0RXIE + UCA0TXIE;  // Enable USCI_A0 TX/RX interrupt   //IE2 |= UCA0RXIE;             // Enable USCI_A0 RX interrupt   //__bis_SR_register(GIE);      // Enter LPM3 w/ interrupts enabled } //串口1发送字符函数 void USCIA1_SendChar(u8 c) {     UCA1TXBUF=c;        while(!(UCA1IFG & UCTXIFG));   // USCI_A0 TX buffer ready?     UCA1IFG &= ~UCTXIFG; } //串口1接收字符函数 u8 USCIA1_ReceiveChar(void) {     u8 data = 0; //阻塞式返回值     while(!(UCA1IFG & UCRXIFG));   // USCI_A0 TX buffer ready?     UCA1IFG &= ~UCRXIFG;     data = UCA1RXBUF;     return data; /*  //非阻塞式返回值     if(UCA1IFG & UCRXIFG)   { UCA1IFG &= ~UCRXIFG; data = UCA1RXBUF; } return data; */     } //串口1发送字符串函数 void USCIA1_SendString(u8 *str) {     while(*str != '\0')     {         USCIA1_SendChar(*str);         str++;     } } #endif //#if USCIA1_EN > 0 /* #include "UART.h" void UART0_Init(void) {     P3SEL = 0x30;                             // 配置P3.4和P3.5脚为串口的输入输出口     UCA0CTL1 |= UCSWRST;                      // 复位串口     UCA0CTL1 |= UCSSEL_1;                     // 选择串口时钟为ACLK(32768HZ)     UCA0BR0 = 0x03;                           // 设置串口波特率为9600     UCA0BR1 = 0x00;                                UCA0MCTL = UCBRS_3+UCBRF_0;                    UCA0CTL1 &= ~UCSWRST;                     // 打开串口     UCA0IE |= UCRXIE;                         // 打开串口接收中断 } */

  • 发表了主题帖: MSP-EXP430F5529LP-GPIO

    代码很简单,改编自TI官网例程,如下所示(编译环境为IAR): #include "io430.h" int main( void ) {   volatile unsigned int i;   // Stop watchdog timer to prevent time out reset   WDTCTL = WDTPW + WDTHOLD;   P1DIR |= BIT0;                            // P1.0 set as output   while(1)                                  // continuous loop   {     P1OUT ^= BIT0;                          // XOR P1.0     for(i=50000;i>0;i--);                   // Delay   }   //return 0; }

  • 发表了主题帖: MSP430F5529 生成PWM波 with CCS

    大概就是通过时钟来产生某个确定频率的PWM波 用FPGA可以得到更完美的波形,不过如果只是提供一个CLK波的话F5529LP就完全可以做到 #include <msp430.h>   unsigned int temp;   int main(void) {       WDTCTL = WDTPW | WDTHOLD;                      //关闭看门狗       //Initialize     P1DIR |= BIT0;       P1OUT |= BIT0;     P1SEL |= BIT2;                                             //从P1.2输出     P1DIR |= BIT2;       //初始化定时器     TA0CTL |= TASSEL_2 + ID_0 ;     TA0CCTL1 = OUTMOD_7;      TA0CCR0 = /*Period*/; //Change me     TA0CCR1 = /*Duty*/;   //Change me       __delay_cycles(2000);     __bis_SR_register(LPM4_bits + GIE);     return 0; }  

  • 发表了主题帖: MSP-EXP430F5529LPPWM库函数+时钟配置

    要实现P2.0口输出10kHz的PWM,这也是应用中电机控制的常用工作频率。要输出准确的频率,了解清楚各个时钟是非常必要的。 首先明确思路,430中有三个时钟:辅助时钟ACLK,频率较低,软件选作各个外围模块的时钟信号,一般用于低速外设;系统主时钟MCLK,频率较高,主要用于CPU和系统,类似于主频;子系统时钟SMCLK,主要用于高速外设模块。这里我们利用TIMER_A产生PWM,选择SMCLK作为模块的时钟源,因此SMCLK的设置就是关键。 LaunchPad为430的XT2外接了一个4MHz的时钟源,T1外接了一个32.768kHz的时钟源,本实验的目标除了输出10kHz的PWM,还要将ACLK配置为32.768kHz,MCLK配置为24MHz,SMCLK配置为4MHz。 整体代码如下: //***************************************************************************** #include "driverlib.h" //***************************************************************************** // //Target frequency for MCLK in kHz // //***************************************************************************** #define UCS_MCLK_DESIRED_FREQUENCY_IN_KHZ   24000   //***************************************************************************** // //MCLK/FLLRef Ratio // //***************************************************************************** #define UCS_MCLK_FLLREF_RATIO   6   //***************************************************************************** // //Variable to store current Clock values // //***************************************************************************** uint32_t clockValue = 0;   //***************************************************************************** // //Variable to store status of Oscillator fault flags // //***************************************************************************** //***************************************************************************** // //XT1 Crystal Frequency being used // //***************************************************************************** #define UCS_XT1_CRYSTAL_FREQUENCY    32768   //***************************************************************************** // //XT2 Crystal Frequency being used // //***************************************************************************** #define UCS_XT2_CRYSTAL_FREQUENCY   4000000 //***************************************************************************** // //Desired Timeout for XT1 initialization // //***************************************************************************** #define UCS_XT1_TIMEOUT 50000 #define TIMER_PERIOD 399 #define DUTY_CYCLE  100 #define UCS_XT2_TIMEOUT 50000 uint16_t status; uint8_t returnValue = 0; void main (void) {     //Stop WDT     WDT_A_hold(WDT_A_BASE);       //Set VCore = 1 for 12MHz clock     PMM_setVCore(PMM_CORE_LEVEL_1);//主频提高后,VCore电压也需要随之配置          //Initializes the XT1 and XT2 crystal frequencies being used     UCS_setExternalClockSource(UCS_XT1_CRYSTAL_FREQUENCY,UCS_XT2_CRYSTAL_FREQUENCY);//设置外部时钟源的频率,没什么实际设定       //Initialize XT1. Returns STATUS_SUCCESS if initializes successfully     GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN4 + GPIO_PIN5);//XT1口不作为普通IO     returnValue = UCS_turnOnLFXT1WithTimeout(UCS_XT1_DRIVE_0,UCS_XCAP_3,UCS_XT1_TIMEOUT);//启动XT1       //Startup HF XT2 crystal Port select XT2     GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN2 + GPIO_PIN3);//XT2口不作为普通IO       //Initialize XT2. Returns STATUS_SUCCESS if initializes successfully     returnValue = UCS_turnOnXT2WithTimeout(UCS_XT2_DRIVE_4MHZ_8MHZ,UCS_XT2_TIMEOUT);//启动XT2          //Set DCO FLL reference = REFO     UCS_initClockSignal(UCS_FLLREF,UCS_XT2CLK_SELECT,UCS_CLOCK_DIVIDER_1);//XT2作为FLL参考          //Set Ratio and Desired MCLK Frequency  and initialize DCO     UCS_initFLLSettle(UCS_MCLK_DESIRED_FREQUENCY_IN_KHZ,UCS_MCLK_FLLREF_RATIO);//MCLK设置为24MHz          //Set ACLK = REFO     UCS_initClockSignal(UCS_ACLK,UCS_REFOCLK_SELECT,UCS_CLOCK_DIVIDER_1);//ACLK设置为32.768kHz          UCS_initClockSignal(UCS_SMCLK,UCS_XT2CLK_SELECT,UCS_CLOCK_DIVIDER_1);//SMCLK设置为4MHz       //P2.0 as PWM output     GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P2,GPIO_PIN0);       //Generate PWM - Timer runs in Up mode     Timer_A_outputPWMParam param = {0};     param.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;     param.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;     param.timerPeriod = TIMER_PERIOD;     param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;     param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;     param.dutyCycle = DUTY_CYCLE;     Timer_A_outputPWM(TIMER_A1_BASE, &param);       // Enable global interrupt     __bis_SR_register(GIE);       //Verify if the Clock settings are as expected     clockValue = UCS_getMCLK();     clockValue = UCS_getACLK();     clockValue = UCS_getSMCLK();       //Loop in place     while (1) ; } 运行结果如下,输出占空比25%,10kHz的方波。

  • 2019-09-10
  • 发表了主题帖: MSP430F5529 ADC参考采样实例

    本帖最后由 fish001 于 2019-9-10 19:05 编辑 采样实例 /*! *     COPYRIGHT NOTICE *     Copyright (c) 2016,CTGU-GB *     All rights reserved. * * * @file       main.c *    MSP430F5529 平台主程序 * @author     CTGU-GB */ #include "include.h" double adcDataTest[20]; /******************************************************************************* *  函数名称:ADC_Filter(u32 num,double *adcDataStorage) *  功能说明:ADC滤波函数 *  作者:klaus 邮箱:xcf2016a@outlook.com *  参数说明:uint8_t num 输入滤波数据个数 *            double *adcDataStorage:滤波数组 *  函数返回:滤波结果 ********************************************************************************/ double ADC_Filter(uint8_t num,double *adcDataStorage) {   uint8_t i,j,k;   uint8_t noswap=1;   double adc_sum_tmp=0,adc_ave_tmp=0;   for(i=0;i<num-1;++i){          for(j=0;j<num-i-1;++j)       {           if(adcDataStorage[j]>adcDataStorage[j+1]){               adcDataStorage[j]=adcDataStorage[j]+adcDataStorage[j+1];               adcDataStorage[j+1]=adcDataStorage[j]-adcDataStorage[j+1];               adcDataStorage[j]=adcDataStorage[j]-adcDataStorage[j+1];               noswap=0;           }        }       if(noswap) break;   }   for(k=2;k<num-2;k++)adc_sum_tmp += adcDataStorage[k];   //adc_sum_tmp -= (adcDataStorage[0]+adcDataStorage[1]+adcDataStorage[num-2]+adcDataStorage[num-1]);       adc_ave_tmp=adc_sum_tmp/(num-4);   adc_sum_tmp=0;   return adc_ave_tmp; } void main() {   DisableInterrupts();          //禁止总中断   LED_Init(LED_ALL);              //LED灯初始化   OLED_Init();   ADC_Init(ADC_CH2,ADC_VREF_3_3V,ADC_10bit);       //初始化通道,P6.1   while(1)   {       int i;     for(i=0;i<20;i++)     {       adcDataTest=ADC_ReadChanelOnce(ADC_CH2)*3.3/1023;       DELAY_MS(10);     }     double ad = ADC_Filter(20,adcDataTest);     OLED_PrintfAt(FONT_ASCII_8X16,0,0,"ADValue:\n%.3f V",ad);         //在指定位置打印   } }  

  • 发表了主题帖: msp430f5529中断笔记

    定义中断服务程序 #pragma vector=PORT1_VECTOR    //P1口中断向量 __interrupt void Port_1(void)            //声明中断服务程序,名为Port_1 { ...                                                    //中断服务程序 } 定时器中断应用例程 #include <msp430.h> void delay(unsigned int i) {     volatile unsigned int j;     for(;i>0;i--)         for(j=0;j<2000;j++);                   // 延时 } int main(void) {   volatile unsigned int i ;   WDTCTL = WDTPW+WDTHOLD;                   // 关闭看门狗   P1DIR |= BIT0;                            // P1.0 输出   P4DIR |= BIT7;                            // P4.7 输出   TA0CCTL0 = CCIE;                          // CCR0中断使能   TA0CCR0 = 50000;   TA0CTL = TASSEL_2 + MC_1 +TACLR;          // SMCLK,增计数模式,清除TAR   __bis_SR_register(LPM0_bits+GIE);          // 进入低功耗模式0,使能中断 } #pragma vector=TIMER0_A0_VECTOR __interrupt void TIMER0_A0_ISR(void)   {     P1OUT ^= BIT0;                          // 反转 P1.0状态     P4OUT ^= BIT7;                          // 反转 P4.7状态     delay(50);   }  

  • 发表了主题帖: MSP430F5529之捕获模式下的HCSR04超声测距

    #include<msp430f5529.h> unsigned int k=0; unsigned int LastCCR1;        //上一次高电平持续的时间,和距离正相关 unsigned int dat[20];        //通过数组备份采集到的数据   void HC_SR04Init(); void HC_SR04Start(); void TimeGetInit();     void main() {     HC_SR04Init();            //初始化超声模块HC_SR04     TimeGetInit();            //初始化计数捕获引脚,模式等     _EINT();     while(1)     {         HC_SR04Start();         LPM0;     } }   /*初始化HC_SR04Init()模块*/ void HC_SR04Init() {     P1DIR|=BIT2; } /*开始超声发送*/ void HC_SR04Start() {     P1OUT|=BIT2;             _delay_cycles(12);        //至少持续10us的高电平     P1OUT&=~BIT2;             }   /*初始化捕获模式,获取时间*/ void TimeGetInit() {     P1DIR&=~BIT4;     P1SEL|=BIT4;       TA0CTL=TASSEL_2+ID_0+MC_2+TACLR;     TA0CCTL3=CM_1+SCS+CAP+CCIE+CCIS_0; }     #pragma vector=TIMER0_A1_VECTOR __interrupt void TIMER0_A1_ISR(void) {     _DINT();                        //关中断     static unsigned char times=1;     static unsigned char i=0;     unsigned char j;     unsigned int t;       if(times==1)     {         LastCCR1=TA0CCR3;        //记录下上次CCR3的值         TA0CCTL3&=~CM_1;        //清上升沿捕获         TA0CCTL3|=CM_2;            //改为下降沿捕获         times++;     }     if(times==0)     {         if(i<20)                //把采集到的20次的值都放到数组中         {             dat[i]=TA0CCR3-LastCCR1;;             i++;         }         if(i>=20)                //为了防止误差,排序把采集到的两头两尾的值去掉,取中间的平均值         {             for(i=0;i<20;i++)             {                 for(j=0;j<20-i;j++)                 {                     if(dat[j]>dat[j+1])                     {                         t=dat[j];                         dat[j]=dat[j+1];                         dat[j+1]=t;                     }                 }             }                          t=0;             for(i=7;i<12;i++)             {                 t=t+dat[i];             }             k=t/5;             i=0;         }         TA0CCTL3&=~CM_2;        //清除下降沿触发         TA0CCTL3|=CM_1;            //变为上升沿触发         times++;                //改变times的值     }     times&=0x01;                //times>1时清0     LPM0_EXIT;                    //退出低功耗模式     TA0CCTL3&=~CCIFG;            //清除中断响应标志     _EINT();                    //开中断 }

  • 2019-09-08
  • 发表了主题帖: CCS调试dsp的一些经验

      在编写DSP子函数时,你也许会使用一些变量,最好不要使用数据量较大的数组,因为DSP在运行子函数程序时,所涉及到的局部变量在内存中有一个临时存储区域,但是DSP不能识别这段区域中有没有别的程序段,这样很可能造成数组被中途截断,一部分数据出错或者程序运行出错。        解决方法:        1、直接定义全局变量,在主程序中分配确定的内存地址;        2、使用指针传递变量,子函数中不要定义新变量;        3、运用函数*UTIL_allocMem(Uint32size)在子函数中分配内存块。 // Allocatememory from the ad-hoc heap void*UTIL_allocMem(Uint32 size) {     void *cPtr;     Uint32 size_temp;     // Ensure word boundaries     size_temp = ((size + 4) >> 2 )<< 2;     if((currMemPtr + size_temp) > ((Uint32)&EXTERNAL_RAM_END))     {         return NULL;     }     cPtr = (void *) (((Uint32)&EXTERNAL_RAM_START) + currMemPtr);     currMemPtr += size_temp;     return cPtr; }

  • 发表了主题帖: TI TMS320F28335的EPWM如何软件强制开关状态

         在DSP用于电机控制的应用中,有时需要用到强制脉宽调制(PWM)的脉冲开关状态这种操作,比如封锁脉冲以停止电力电子开关管工作;       在上电的初始时刻,经常也需要对PWM状态进行一种强制的初始化,比如强制高、强制低等等;一些特殊的PWM算法,如果不是基于三角      载波比较法的,如一些SHEPWM、滞环PWM方法等等,也需要直接输出PWM脉冲的状态,此时不能使用比较值与定时器的直接比较来得到 开关状态,而且在需要的开关状态已知情况下,直接强制PWM状态,即可得到需要PWM脉冲。       在基于事件管理器(EV)的TI C2000 DSP中,如TMS320LF2407A、F2810、2812中,强制脉冲状态是一件很简单的事情,直接写ACTRx寄存器就可以了,比如:EvaRegs.ACTRA.all=0xfff;这样的语句就能强制EVA对应的6个PWM管脚全部为高电平,等于0则可以全部强制低电平,需要强制特定管脚的值只需要修改对应的位即可。在基于改进的PWM模块的C2000 DSP中,如2833x系列,都使用了新型的增强型PWM模块(EPWM),其每个PWM管脚都可以有单独的配置,       这样就造成了PWM配置的复杂性增加了。在网上找了好久,都没有找到如何强制脉冲状态。本来想,换了个新片子,大不了照葫芦画瓢配置一番就行了,于是拿过EPWM的手册,照着AQSFRC寄存器配置了半天,却总是没反应。看名字,AQSFRC是Action-Qualifier Software Force Register, 应该是可以force它听话的啊!代码 EPwm1Regs.AQSFRC.bit.OTSFA=1; EPwm1Regs.AQSFRC.bit.ACTSFA=1; 这样子的根本不产生任何效果。 后来才发现是研究不深啊,原来真正需要配置的是AQCSFRC: EPwm1Regs.AQSFRC.all=0xc0; EPwm1Regs.AQCSFRC.all=state; 两个寄存器的datasheet的页码就差一页,下次真得要读仔细了。

  • 发表了主题帖: 嵌入式uboot学习

    1、计算机系统的主要部件 (1)、计算机系统就是由CPU来做核心进行运行的系统。典型的计算机系统有:PC机(台式机+笔记本)、嵌入式设备(手机、平板电脑、游戏机)、单片机(家用电器如电饭锅、空调)。 (2)、计算机系统的组成部件非常多,不同的计算机系统组成部件也不同。但是所有的计算机系统运行时需要的主要核心部件都是3个东西:CPU + 外部存储器(Flash/硬盘)+ 内部存储器(DDR SDRAM/SRAM)。 2、PC机的启动过程 (1)、典型的PC机部署:BIOS程序部署在PC机主板上(随主板出厂时已经预制了),操作系统部署在硬盘上,内存在掉电时无作用,CPU在掉电时不工作。 (2)、启动过程:PC机上电后先执行BIOS程序(实际上PC的BIOS就是NorFlash),BIOS程序负责初始化DDR内存,负责初始化硬盘,然后从硬盘上将操作系统OS镜像读取到DDR中,然后跳转到DDR中去执行操作系统OS直到启动(OS启动后BIOS就无用了) 3、典型嵌入式Linux系统启动过程 (1)、嵌入式系统的部署和启动都是参考PC机的。只是设备上有一些差别 (2)、典型嵌入式系统的部署:uboot程序部署在Flash(能作为启动设备的Flash)上,操作系统OS部署在Flash(嵌入式系统中用Flash代替了硬盘)上,内存在掉电时无作用,CPU在掉电时不工作。 (3)、启动过程:嵌入式系统上电后先执行uboot,然后uboot负责初始化DDR、初始化Flash,然后将操作系统OS从Flash中读取到DDR中,然后启动OS(OS启动后uboot就无用了)。 总结:嵌入式系统和PC机的启动过程几乎没有两样,只是BIOS换成了uboot(bootloader中的一种),硬盘换成了Flash。 4、Android系统启动过程 (1)、Android系统的启动与Linux系统几乎一样。前边完全一样,只是在内核启动后加载根文件系统后不一样了。 (2)、可以认为启动分为两个阶段:第一个阶段是uboot到OS启动;第二个阶段是OS启动后到根文件系统rootfs加载到命令行执行。 5、uboot作用 (1)、用来启动操作系统内核 (2)、负责部署整个计算机系统 (3)、操作Flash等板子硬件驱动。 (4)、提供一个命令行界面供人来操作。 6、uboot从哪里来? (1)、uboot是SourceForge上的开源项目。 (2)、uboot项目的作者:一个德国人最早发起的项目 (3)、uboot就是由一个人发起,然后由整个网络上所有感兴趣的人共同维护发展而来的一个bootloader。 7、uboot必须解决哪些问题? (1)、自身可开机直接启动   a、一般的片上系统SoC都支持多种启动方式,譬如SD卡启动,NorFlash启动、NandFlash启动等等,uboot要能够开机启动,必须根据具体的SoC的启动设计来设计uboot。   b、uboot必须进行和硬件相对应的代码级别的更改和移植,才能够保证可以从相应的启动介质启动。uboot中第一阶段的start.S文件中具体处理这一块。 (2)、能够引导操作系统内核启动并给内核传参   a、uboot的终极目标就是启动内核   b、Linux内核在设计的时候,设计为可以被传参。也就是说我们可以在uboot中事先给Linux内核准备一些启动参数放在内存中特定位置然后传给内核,内核启动后会到这个特定位置去取uboot传给它的参数,然后在内核中解析这些参数,这些参数将被用来指导Linux内核的启动过程。 (3)、能提供系统部署功能   a、uboot必须能够被人借助而完成整个系统(包括uboot、kernel、rootfs等的镜像)在Flash上的烧入下载工作。   b、刷机就是利用uboot中的fastboot功能将各种镜像烧入到iNand中,然后从iNand启动。 (4)、能进行SoC级和板级硬件管理   a、uboot中实现了一部分硬件的控制能力(uboot中初始化了一部分硬件),因为uboot为了完成一些任务必须让这些硬件工作。譬如uboot要实现刷机必须能驱动iNand,uboot要实现网络功能就必须驱动网卡芯片。   b、SoC级(譬如串口)就是SoC内部外设,板级就是SoC外部开发板上面的硬件(譬如网卡、iNand) 8、uboot的入口和出口。   uboot的入口就是开机自动启动,uboot的唯一出口就是启动内核。uboot还可以执行很多级别的任务(譬如烧入系统),但是其他任务执行完后都可以回到uboot的命令行继续执行uboot命令,而启动内核命令一旦启动就回不来了。 总结:   uboot经过多年发展,已经发展成为业内的bootloader标准。现在大部分的嵌入式设备都会默认使用uboot来作为bootloader。uboot的最终目的就是为了启动内核。

  • 发表了主题帖: C语言 指针理解

    1、指针   指针全称是指针变量,其实质是C语言的一种变量。这种变量比较特殊,通常他的值会被赋值为某个变量的地址值(p = &a),然后我们可以使用 *p 这样的方式去间接访问p所指向的那个变量。 2、为什么需要指针?        指针存在的目的就是间接访问。有了指针之后,我们访问变量a不必只通过a这个变量名来访问。而可以通过p = &a; *p = xxx;这样的方式来间接访问变量a。 3、指针的定义和初始化   指针既然是一种变量,那么肯定可以定义,也可以初始化 第一种:先定义再赋值   int *p;     //定义指针变量p   p = &a;  //给p赋值 第二种:定义的同时  初始化        int *p = &a;        //效果等同于上边的先定义再赋值                           如果前边没有int 只有 *p = &a;这样是错误的 4、int *p;       int* p;       int * p; 以上三种定义方式都对。 5、指针定义的两种理解方法 int *p; 第一种:首先看到p,这个是变量名;其次,p前边有个*,说明这个变量是一个指针变量;最后, *p前边有一个int,说明这个指针变量p所指向的是一个int型数据。 第二种:首先看到p,这个是变量名;其次看到p前边的int *,把int *作为一个整体来理解,int * 是一种类型(复合类型),该类型表示一种指向int型数据的指针。 5、实例 int main() {   int a = 23;               int *p;                                //定义一个int型的指针变量p   p = &a;                             //将变量a的地址赋给p;   *p = 111;                          //*p 代表指针变量p所指向的那个变量,在这里也就是变量a; 也就是a =  111;         printf("a = %d\n",a);        return 0; } 运行结果是 a=111;

  • 发表了主题帖: 为什么说破解单片机很容易

          MCU的安全等级正在逐步提升,一些公司甚至推出了安全主控,这是很好的现象,说明大家越来越重视嵌入式领域的信息安全和程序安全了。但对于很多特殊行业,比如消费类电子产品,低成本的通讯模块、电源控制模块等等,迫于成本压力以及更新换代速度问题,都无法使用更安全的主控MCU,有很大一部分产品甚至还在使用51单片机。   大家可能都知道破解51单片机是很容易的,但为什么容易,又是如何来破解的,可能很多人就不大清楚了,我在这里结合网上一些前辈整理的资料,和自己的经验,对MCU破解技术做个简单分析。   大家不要把解密想的很复杂,他不像研发一款产品那样,先确定客户需求或者新产品主要功能,然后立项确定技术指标,分配软硬件开发任务,基于硬件调试程序,然后验证功能,测试bug,还要做环境试验。行业里解密的方法有很多,每个人破解的思路也不一样。但是大致分为几种。   1软件破解   利用软件破解目标单片机的方法,利用这种方法,不会对目标MCU元器件造成物理损伤。主要是对WINBONGD,SYNCMOS单片机和GAL门阵列,这种利用软件解密设备,按照一定的步骤操作,执行片内的程序送到片外的指令,然后用解密的设备进行截获,这样芯片内部的程序就被解密完成了(GAL采用逻辑猜测),就可以得到加密单片机中的程序。   2硬件破解   流程如下:   1、测试   使用高档编程器等设备测试芯片是否正常,并把配置字保存。   2、开盖   采用手工或专用开盖设备进行开盖处理,这里说的开盖并不是说单片机或者其他MCU真有一个盖。简单解释一下,MCU其实是一个大规模集成电路,它是由N个电路组合而成的,而晶圆就是搭载集成电路的载体。将晶圆进行封装后,就形成了我们日常所用的IC芯片,封装形式可以有多种,比如TSSOP28、QFN28等,大家可以自己去百度搜索,这里不再复述。   3、做电路修改   对不同芯片,提供对应的图纸,让厂家做电路修改,目的是让MCU的存储区变得可读。有些MCU默认不允许读出Flash或者E2PROM中的数据,因为有硬件电路做保护,而一旦切断加密连线,程序就暴露可读了。如图2所示   (切割掉加密熔丝,这样就可以直接读出芯片内部程序)   4、读程序   取回修改过的MCU,直接用编程器读出程序,可以是HEX文件,或者BIN文件。   5、烧写样片给客户   按照读出的程序和配置,烧写到目标MCU中,这样就完成了MCU的破解。至此,硬件破解法成功完成。   3软硬兼施   采用软件和硬件结合的方法,需要对芯片的内部结构非常的熟悉。   另外还有其他一些破解技术,例如电子探测攻击、过错产生技术等等,但是最终目的只有一个,就是能够模仿出目标MCU的功能就可以了。   看到这里大家应该明白一个道理,破解MCU并不能做到把MCU中的程序原封不动的还原出来。目前的技术也做不到,至少国内应该做不到。针对以上情况,加密芯片应运而生,初期确实能很好的保护MCU的安全,但很快就被找到了漏洞。   我举个实际破解的例子分析一下,大家就能够明白了。   加密原理:   MCU和加密芯片各存储一条认证秘钥,存储同样的加密算法;MCU产生随机数发给加密芯片,后者用秘钥加密后将密文返回,此时MCU解密后,比对明文是否和生成的随机数相等。如果相等,程序正常运行;如果不相等,出错处理。   因为盗版商没有这条秘钥,加密芯片与MCU交互的数据又是随机变化的,无法找到规律,所以只能把加密芯片的程序破解了,再复制一片加密芯片才能让MCU的程序跑起来。而加密芯片不同于通用MCU,它内部有很多安全机制,破解难度非常大。   这种加密方案看似非常安全,但其实还是有漏洞的。   破解方法:   首先按照第二种破解方法,获取到MCU的HEX文件。此处省略N步,不再复述。   使用软件进行HEX反编译,反编译软件目前有很多。   在反编译的程序中,找到对比点,比如图3所示,CJNE语句可能就是这个对比点。因此只要把箭头2那行语句删除,然后重新把汇编语言下载到MCU中,破解工作就完成了。此时即使没有加密芯片,MCU也能正常运行了。   其实原因很简单。MCU是要对加密芯片的返回值进行判断的,那么不让他做判断,这样一来不管加密芯片返回值是什么,程序都能正常运行。   因此这种加密方案很快就被破解了。当然也不是这么绝对,因为有些MCU即使剖片也不能获得里面的HEX或者BIN文件,所以这种破解方案也要看MCU的安全等级够不够高。但是足以说明一个问题,这种通过对比加密结果来实现加密的方案,安全等级还是不够高,还是有破解漏洞的。   因为篇幅有限,本期只做解密技术的简单介绍。所谓知己知彼,百战百胜,唯有了解了破解技术,才能更有效的做加密防护。

  • 2019-09-07
  • 回复了主题帖: TMS320F2812求教

    片言只语无法解答 编译出错

  • 发表了主题帖: DSP编程中volatile的用法详解

    一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 1). 一个参数既可以是const还可以是volatile吗?解释为什么。 2). 一个指针可以是volatile 吗?解释为什么。 3). 下面的函数有什么错误: int square(volatile int *ptr) { return *ptr * *ptr; } 复制代码 下面是答案: 1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 3). 这段代码的有个恶作剧。这段代码的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码: int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; } 复制代码 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: long square(volatile int *ptr) { int a; a = *ptr; return a * a; } 复制代码 volatile的本意是“易变的” ,由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。 比如: static int i=0; int main(void) { … while (1) { if (i) dosomething(); } } /* Interrupt service routine. */ void ISR_2(void) { i=1; } 复制代码 程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被 调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。 一般说来,volatile用在如下的几个地方: 1、中断服务程序中修改的供其它程序检测的变量需要加volatile; 2、多任务环境下各任务间共享的标志应该加volatile; 3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义; 另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实 现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。 volatile 的含义 volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用: 1 、不会在两个***作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器 自己无法知道,volatile就是告诉编译器这种情况。 2 、不做常量合并、常量传播等优化,所以像下面的代码: volatile int i = 1; if (i > 0) … if的条件不会当作无条件真。 3 、对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值***作,然而对Memory Mapped IO的处理是不能这样优化的。 前面有人说volatile可以保证对内存操作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc。 对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。 你可能不知道在Pentium及后续CPU中,下面两组指令 inc jiffies ;; mov jiffies, %eax inc %eax mov %eax, jiffies 作用相同,但一条指令反而不如三条指令快。 ================================================================================ 关键在于两个地方: 编译器的优化 (请高手帮我看看下面的理解) 在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值; 当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致 当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致 当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致 举一个不太准确的例子: 发薪资时,会计每次都把员工叫来登记他们的银号;一次会计为了省事,没有即时登记,用了以前登记的银号;刚好一个员工的银丢了,已挂失该银号;从而造成该员工领不到工资 ; 员工 -- 原始变量地址 银**号 -- 原始变量在寄存器的备份 在什么情况下会出现(如1楼所说) 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 补充: volatile应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人; “易变”是因为外在因素引起的,象多线程,中断等,并不是因为用volatile修饰了的变量就是“易变”了,假如没有外因,即使用volatile定义,它也不会变化;而用volatile定义之后,其实这个变量就不会因外因而变化了,可以放心使用了; 大家看看前面那种解释(易变的)是不是在误导人; volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。 使用该关键字的例子如下: int volatile nVint;
    当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 例如:
    volatile int i=10; int a = i; … //其他代码,并未明确告诉编译器,对i进行过操作 int b = i; 复制代码 >>>>volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。 >>>>注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响: 1 2
    首先,用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码:
    #include <stdio.h> void main() { int i=10; int a = i; printf(“i= %d”,a); //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 __asm { mov dword ptr [ebp-4], 20h } int b = i; printf(“i= %d”,b); } 复制代码 然后,在调试版本模式运行程序,输出结果如下: i = 10 i = 32 然后,在release版本模式运行程序,输出结果如下: i = 10 i = 10 输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。下面,我们把 i的声明加上volatile关键字,看看有什么变化: #include <stdio.h> void main() { volatile int i=10; int a = i; printf(“i= %d”,a); __asm { mov dword ptr [ebp-4], 20h } int b = i; printf(“i= %d”,b); } 复制代码 分别在调试版本和release版本运行程序,输出都是: i = 10 i = 32 这说明这个关键字发挥了它的作用! volatile对应的变量可能在你的程序本身不知道的情况下发生改变 比如多线程的程序,共同访问的内存当中,多个程序都可以操纵这个变量 你自己的程序,是无法判定合适这个变量会发生变化 还比如,他和一个外部设备的某个状态对应,当外部设备发生操作的时候,通过驱动程序和中断事件,系统改变了这个变量的数值,而你的程序并不知道。 对于volatile类型的变量,系统每次用到他的时候都是直接从对应的内存当中提取,而不会利用cache当中的原有数值,以适应它的未知何时会发生的变化,系统对这种变量的处理不会做优化——显然也是因为它的数值随时都可能变化的情况。 1 2 典型的例子 for ( int i=0; i<100000; i++); 这个语句用来测试空循环的速度的 但是编译器肯定要把它优化掉,根本就不执行 如果你写成 for ( volatile int i=0; i<100000; i++); 它就会执行了;

  • 发表了主题帖: KEIL中如何建立多个C文件及注意事项 模块化编程

          首先,我们需要一个新文档,这个文档的建立有两种方法,下面将以delay函数进行叙述:       第一种,在工程目录下建立一个delay1ms.txt然后将其改名为delay1ms.h。因为都是同编码的所以不会出现乱码,然后在工程中将其打开。       第二种方法是直接在工程中新建一个文档,然后保存的时候将名字保存为delay.h即可。其次,我们需要编写delay.h这个文件的内容,其内容如下: #ifndef _DELAY1MS_H_ #define _DELAY1MS_H_ void delay1ms();//延时函数 #endif         这个是头文件的定义,作用是声明了delay1ms()函数,因为如果在别的函数中如果我们需要用到delay1ms()函数的话,若不事先声明则在编译的时候会出错。对于#ifndef……#define……#endif;这个结构的意思就是说如果没有定义(宏定义)一个字符串,那么我们就定义它,然后执行后面的语句,如果定义过了那么就跳过不执行任何语句。   注意事项说明:         一、当你在A文件的C文件中定义了一个变量flag,但是要在另一个B文件的C文件里调用它,这时你要在B文件进行声明,表示你已经定义了flag这个变量。例如: A中定义: unsigned char flag;    B中声明:extern unsigned char flag; 注意B中要加上变量的类型,不然有时会报错。          二、.h文件里一般只放函数声明和全局变量,具体功能的实现一般写在.c文件(函数)里。  

  • 发表了主题帖: c语言模块化编程

    本帖最后由 fish001 于 2019-9-7 21:44 编辑 在刚接触单片机,刚编写代码的时候都只是有一个.h 和一个.c文件。把所有的函数都写在.c里面。这样遇到复杂的系统时,代码量就很大,一下子就是几百行。而且把所有函数写在一个.c里不利于移植,对于以后的开发有了很大的限制。为了解决这个缺点就必须学会模块化编程。将程序分解成一个一个模块。这样就有了多个.c 和多个.h。下面开始讲解什么是模块化编程,怎么去模块化。 借用网上的流水灯例子来说明,侵删。 在不模块化情况下:   #include <reg52.h> #include "intrins.h" #define uchar unsigned char #define uint unsigned int void Delay_Ms(uint xms) {     uint    i,j; for(i=0;i<xms;i++)     {         for(j=0;j<110;j++);     } } void Display_Led() {     uchar    aa,j;     aa = 0x7f;     for(j=0;j<8;j++)     {         P1  = aa;         aa = _cror_(aa,1); Delay_Ms(500);     } } void main() {     while(1)     {         Display_Led();     } } 上面这个程序是全部写在了main.c这一个C文件里。完成了一个流水灯功能,程序非常简单,那么下面我们将程序模块化。模块化我们就建立一个   main.c    led.c    led.h    这三个文件,那么这三个文件里分别写什么东西呢,下面一个一个的写。led.h 里内容如下: #ifndef LED_H             //理解为    if not define led.h #define LED_H             //如果没有定义 led.h ,这里就定义一个 led.h   #include "reg52.h"        //由于  led,c 里面要用到 51单片机寄存器    P1,                 // 所以这里包含    51 单片机的头文件 #include "intrins.h"        // 循环移位用到 #define    uchar unsigned char #define    uint unsigned int    // 这些宏定义都可以在头文件 .h 里定义 void Delay_Ms(uint    xms); void Display_Led();        // 将led.c 里的函数在led.h 里面申明   #endif    // 定义结束 led.c 里内容如下,将流水灯的主要程序放在这个.c 里面。作为单独的流水灯模块,在以后的程序中如果要用到这个流水灯程序 就直接把 led.c 和 led.h 移植过去。 #include  "led.h"    // 这里包含 led.h 这个头文件 void Delay_Ms(uint xms) {     uint    i,j; for(i=0;i<xms;i++)     {         for(j=0;j<110;j++);     }     }   void    Display_Led() {     uchar    aa,j; aa = 0x7f; for(j=0;j<8;j++)     {         P1  = aa;         aa = _cror_(aa,1);Delay_Ms(500);     } } 用来完成流水灯功能的模块程序已经写好了,现在就开始主函数的编写。主函数就只需要调用一个Display_Led()函数就行了,看现在是怎么写的: #include "led.h"        //在  main.c 里现在包含的也是 led.h 而不是 reg52.h了 void main() {     while(1)     {         Display_Led();    // 这样直接调用就行。     } } 模块化编程的时候要想到你把一个 .c 和.h 文件给另一个人,那个人能够很轻松的运用你给的这个模块来实现效果,且不需要去改动很多东西,那就算成功了。 extern是什么:extern文件间的全局变量。因此,.c里面用到的变量名字,一般在对应的.h文件中用extern声明(只声明不定义)  

  • 发表了主题帖: CC2640R2用 IDE 的 post-build 功能来生成单个固件文件

    CC2640 R2是一款面向 Bluetooth Smart 应用的低功耗无线 MCU。该芯片运行TI的BLE协议栈,同时具有OAD(Over the Air Download)空中固件升级功能。 CCS是TI提供的强大的MCU/Processor免费软件开发IDE,支持TI全系列的MCU和Processor。IAR是IAR公司提供的商用软件开发IDE。CC2640R2可以选用CCS或者IAR任意一款进行开发。 为了提高OAD的效率,同时为了更合理的代码架构,TI的BLE例程都分为Application和Stack两个工程。但是两个工程和两个固件,往往会造成生产或者OAD的不便,在此我们提供一个小窍门来改善一下这个不便。 首先我们看一下IAR/CCS的编译结果: IAR和CCS是开发TI的BLE方案的IDE工具。在代码编译完成后,都能生供下载调试用的.out文件,还有.hex文件和.bin文件。 其中.out文件带了调试信息,主要是下载调试用。.hex文件和.bin文件都可以用于生产下载,但是.hex文件又带了地址信息,crc校验等额外信息,所以.out和.hex都会比.bin文件大很多。因此最理想的生产下载文件其实还是.bin文件,同样,对于目前CC2640R2F的OAD功能来说,.bin文件也依然是最佳选择。  图 1.   编译结果   我们再来看一下CC2640R2带OAD特性的工程: CC2640R2带OAD特性的工程由三个子工程构成,BIM,application和stack。 BIM其实就是bootloader,app就是application,stack就是协议栈。 这样的好处是升级的时候可以只单独更新application部分,并且能大大提高OAD的速度(因为对大多数客户来说实际只需要更新application部分)。 但是这样做的结果就有了三个独立image文件,这会对最终的生产造成困扰。好在IAR或CCS等IDE工具提供了一些方法,能使我们灵活在.hex文件和.bin文件之间互相转换,合并等等,这就是它们提供的post-build工具:post-build顾名思义就是build之后的动作。 先来看一下IAR/CCS的Post-build工具: 以Application工程为例。 IAR的Post-build工具:                                                                                                                                                          图 2.     IAR Post-build CCS的Post-build 工具:                                                                                                                                                         图 3.     CCS Post-build 把IAR和CCS的Post-build 内容拿出来,分别是: IAR的Post-build内容: cmd /C "$TOOLS_BLE_DIR$\output_converter\output_converter.exe $EXE_DIR$\sp_oad_offchip_$PROJ_FNAME$_$CONFIG_NAME$.bin & $TOOLS_BLE_DIR$\oad\oad_image_tool.exe iar $PROJ_DIR$ 1 $EXE_DIR$\$TARGET_BNAME$.hex $PROJ_DIR$\..\config\iar_boundary.xcl $PROJ_DIR$\..\stack\FlashROM\Exe\sp_oad_offchip_cc2640r2lp_stack_FlashROM.hex -o $EXE_DIR$\$TARGET_BNAME$_oad"   CCS的Post-build内容: ${TOOLS_BLE_DIR}/oad/oad_image_tool        ccs ${PROJECT_LOC} 1         FlashROM/${ProjName}_${ConfigName}.hex         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_stack/TOOLS/ccs_compiler_defines.bcfg         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_stack/FlashROM/simple_peripheral_cc2640r2lp_oad_offchip_stack_FlashROM.hex         -o ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/${ProjName}_${ConfigName}_oad 上面标红的就是CC2640R2的SDK提供的一些脚本工具,可以在这个目录中找到:C:\TI\simplelink_cc2640r2_sdk_1_40_00_45\tools\blestack。分别是 output_converter.exe和 oad_image_tool.exe。 SDK提供的这几个.exe文件都是Python文件转换过来的,我们也可以把自己用Python转成的.exe文件放这里来执行。 上述脚本工具在Post-build中的应用 output_converter.exe的用法: output_converter.exe的作用:把.bin文件转换成.hex文件。 用windows的command line可以看到它的参数配置: 图 4.     output_converter.exe的参数配置 oad_image_tool.exe的用法: oad_image_tool.exe的作用:把特定的1个,或者多个.hex文件合并,新生成一个.hex文件,并转换成加上CRC32校验信息的.bin文件,注意的是,新生成的.hex文件是没有加上CRC32校验信息的,只有新生成的.bin文件带。 它是特别指定了OAD要用的BIM,application,stack三个工程的某种组合,且合并的话,只能合并application和stack工程的image,所以这个工具并不是通用.hex转.bin工具,只能配合OAD用(文章后面会有通用.hex转.bin工具介绍)。 图 5.     oad_image_tool.exe的参数配置 *注意:这里说的都是application工程,编译application工程之前,stack工程必须先编译完成。因为很显然,stack工程的.hex文件是其中的一个参数。 output_converter.exe & oad_image_tool.exe的配合: IAR的工程配置里面,配置的是编译好默认输出.bin文件,所以需要先用output_converter.exe把.bin文件转换成.hex。 CCS则是直接生成.hex文件,CCS只要调用oad_image_tool.exe转换成带CRC32校验信息的.bin文件就可以了。 所以最后总结,不管是IAR还是CCS,都是通过post-build来转换application和stack工程的编译结果,并产生这两个工程单个合并的.hex和.bin文件(BIM工程并不在合并之列),产生的.bin文件是带OAD需要的CRC32的image校验信息的,而.hex文件是不带这个CRC32校验信息的。 在Post-build中活用脚本工具: 用hex的方法烧录 如果用.hex的方式去烧录,这中间就有两个问题: 第一就是还是得烧录两个.hex文件:BIM的.hex,和application+stack合并的.hex。 第二就是,默认的post-build产生的.hex文件不带CRC32信息,如果直接把最终产生的单个.hex文件和BIM工程的.hex文件烧录至芯片,由于application镜像的起始位置不带有效的CRC32信息,因此BIM会认为这是无效的镜像,最后造成无法跳转到application去执行。 要解决这两个问题,就得在application+stack的.hex起始位置加上有效CRC32校验信息,然后把BIM的.hex文件和application+stack的.hex文件合并,变成真正的单个.hex,这样直接下载就没问题了。 解决方法: 首先,给application+stack的.hex文件头部相应位置加入CRC32信息。 实际上经过oad_image_tool.exe合并的application+stack的.bin文件的头部已经有CRC32校验信息,而且output_converter.exe能把.bin文件转成.hex文件,所以最快的方法自然是用output_converter.exe把已经合并的application+stack的.bin文件再转换成.hex就行了: $TOOLS_BLE_DIR$\output_converter\output_converter.exe -o $EXE_DIR$\$TARGET_BNAME$_oad_merged_crc32.hex $EXE_DIR$\$TARGET_BNAME$_oad_merged.bin" 这个产生的.hex就是application+stack,并带CRC32信息。 接下来就是把BIM的.hex和application+stack带CRC32信息的.hex 合并。 由于SDK提供的oad_image_tool.exe是一个特殊的工具,并不能随意合并任意的.hex文件,所以不能用这个工具来合并BIM和application+stack的.hex文件。这里提供一个能合并任意.hex文件的工具:hexmerge.exe,这个也是通过python转换的.exe文件,后面会介绍怎么转换的。 把hexmerge.exe放到对应目录下,比如:C:\TI\simplelink_cc2640r2_sdk_1_40_00_45\tools\blestack\oad 用法: $TOOLS_BLE_DIR$\oad\hexmerge.exe -o $EXE_DIR$\bim_app_stack_crc32.hex $PROJ_DIR$\..\bim_offchip\ FlashOnly \Exe\sp_oad_offchip_cc2640r2lp_bim_offchip_FlashOnly.hex $EXE_DIR$\$TARGET_BNAME$_oad_merged_crc32.hex   把上面两条命令加入到post-build中去,下面蓝色部分: cmd /C "$TOOLS_BLE_DIR$\output_converter\output_converter.exe $EXE_DIR$\sp_oad_offchip_$PROJ_FNAME$_$CONFIG_NAME$.bin & $TOOLS_BLE_DIR$\oad\oad_image_tool.exe iar $PROJ_DIR$ 1 $EXE_DIR$\$TARGET_BNAME$.hex $PROJ_DIR$\..\config\iar_boundary.xcl $PROJ_DIR$\..\stack\FlashROM\Exe\sp_oad_offchip_cc2640r2lp_stack_FlashROM.hex -o $EXE_DIR$\$TARGET_BNAME$_oad &$TOOLS_BLE_DIR$\output_converter\output_converter.exe -o $EXE_DIR$\$TARGET_BNAME$_oad_merged_crc32.hex $EXE_DIR$\$TARGET_BNAME$_oad_merged.bin & $TOOLS_BLE_DIR$\oad\hexmerge.exe -o $EXE_DIR$\bim_app_stack_crc32.hex$PROJ_DIR$\..\bim_offchip\FlashOnly\Exe\sp_oad_offchip_cc2640r2lp_bim_offchip_FlashOnly.hex $EXE_DIR$\sp_oad_offchip_cc2640r2lp_app_FlashROM_oad_merged_crc32.hex" 这样,最终就能产生出一个bim+application+stack三个工程合并后的.hex文件,并且带CRC32校验,一次性下载到芯片后能直接运行。 用bin的方法烧录   如果用.bin的方式烧录,那么CRC32的问题是不存在的,存在的唯一问题就是: BIM的.bin和application+stack的.bin是两个独立的文件,没法同时烧录,也没法分别烧录,因为.bin文件是不带地址信息的,比较麻烦。 最好的方法当然是把这几个.bin文件合成一个,再进行烧录。但SDK提供的oad_image_tool.exe又不能直接把BIM合并进去产生.bin文件,所以还是需要借助其他工具。 其实根据前面的启发,最快的方法,只要把前面的过程中产生的BIM+application+stack并带CRC32校验的.hex文件直接转换成.bin文件就行了。我们借用hex2bin.exe这个工具,同样,也是python转换过来的.exe。 解决方法: 首先,比如也把hex2bin.exe放到下面目录中: C:\TI\simplelink_cc2640r2_sdk_1_40_00_45\tools\blestack\oad 用法: $TOOLS_BLE_DIR$\oad\hex2bin.exe $EXE_DIR$\bim_app_stack_crc32.hex $EXE_DIR$\bim_app_stack_crc32.bin 那么把这条命令加到post-build中去: cmd /C "$TOOLS_BLE_DIR$\output_converter\output_converter.exe $EXE_DIR$\sp_oad_offchip_$PROJ_FNAME$_$CONFIG_NAME$.bin & $TOOLS_BLE_DIR$\oad\oad_image_tool.exe iar $PROJ_DIR$ 1 $EXE_DIR$\$TARGET_BNAME$.hex $PROJ_DIR$\..\config\iar_boundary.xcl $PROJ_DIR$\..\stack\FlashROM\Exe\sp_oad_offchip_cc2640r2lp_stack_FlashROM.hex -o $EXE_DIR$\$TARGET_BNAME$_oad &$TOOLS_BLE_DIR$\output_converter\output_converter.exe -o $EXE_DIR$\$TARGET_BNAME$_oad_merged_crc32.hex $EXE_DIR$\$TARGET_BNAME$_oad_merged.bin & $TOOLS_BLE_DIR$\oad\hexmerge.exe -o $EXE_DIR$\bim_app_stack_crc32.hex$PROJ_DIR$\..\bim_offchip\FlashOnly\Exe\sp_oad_offchip_cc2640r2lp_bim_offchip_FlashOnly.hex $EXE_DIR$\sp_oad_offchip_cc2640r2lp_app_FlashROM_oad_merged_crc32.hex & $TOOLS_BLE_DIR$\oad\hex2bin.exe $EXE_DIR$\bim_app_stack_crc32.hex $EXE_DIR$\bim_app_stack_crc32.bin" 所以总结起来,只要把上面的这一整段命令复制,粘帖到IAR的post-build配置中去,我们就能通过IAR的一次编译,自动同时可以得到一个合并了BIM,application,stack的完整的.hex和.bin文件(bim_app_stack_crc32.hex和bim_app_stack_crc32.bin),可以用于烧录。 图 6.     IAR 最终生成的编译结果 在CCS中的灵活用法 前面是基于IAR的配置方式,基于CCS的本质上也是一样,在post-build中,加入: ${TOOLS_BLE_DIR}/oad/oad_image_tool         ccs ${PROJECT_LOC} 1         FlashROM/${ProjName}_${ConfigName}.hex         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_stack/TOOLS/ccs_compiler_defines.bcfg         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_stack/FlashROM/simple_peripheral_cc2640r2lp_oad_offchip_stack_FlashROM.hex        -o ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/${ProjName}_${ConfigName}_oad ${TOOLS_BLE_DIR}/output_converter/output_converter         -o ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/${ProjName}_${ConfigName}_oad_merged_crc32.hex         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/${ProjName}_${ConfigName}_oad_merged.bin   ${TOOLS_BLE_DIR}/oad/hexmerge        -o ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/bim_app_stack_crc32.hex         ${WORKSPACE_LOC}/bim_oad_offchip_cc2640r2lp_app/FlashOnly/bim_oad_offchip_cc2640r2lp_app.hex         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/${ProjName}_${ConfigName}_oad_merged_crc32.hex   ${TOOLS_BLE_DIR}/oad/hex2bin         ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/bim_app_stack_crc32.hex ${WORKSPACE_LOC}/simple_peripheral_cc2640r2lp_oad_offchip_app/FlashROM/bim_app_stack_crc32.bin 这样,编译完application工程后,就能自动得到合并了BIM+application+stack的.hex和.bin文件(bim_app_stack_crc32.hex和bim_app_stack_crc32.bin)。 图 7.     CCS 最终生成的编译结果 结论: IDE工具的Post-build往往在实际开发过程中容易被忽略,其实只要灵活应用,还是能帮助我们做很多事情的,比如本文所说针对CC2640R2F的固件的生成,灵活应用Post-build功能就能方便产生我们自己需要的固件,提高工作效率。   另附 Python的独立可执行.exe脚本文件 如何通过Python得到可直接独立执行,不需要python环境的.exe文件?可以用Pyinstaller这个工具,Pyinstaller是一个可以把Python脚本文件打包成.exe的工具。        Python环境下载:https://www.python.org/        Pyinstaller下载:http://www.pyinstaller.org/ 这里用到的hexmerge和hex2bin,用的是python的intelhex库:https://pypi.python.org/pypi/IntelHex

  • 2019-09-05
  • 发表了主题帖: C2000系列(28335)DSP的Flash_API的限制

    API可以: 1、可以运行在静态内部SARAM中 2、配置真确的CPU频率 3、根据Flash_API列表去集成API到应用中 4、初始化PLL控制寄存器,在使用API函数前等待PLL锁住 5、初始化API回调函数指针(Flash_CallbackPtr),如果不 不用回调函数请指向NULL。如果初始化失败将会导致程序跳转到未定义位置 6、仔细阅读API关于回调函数、中断、看门狗等的限制 API不可以: 1、不能执行在flash或者OTP中,如果APIs存储在flash或者OTP中,使用前请复制到SARAM中。 2、在擦写、编程或者其他API函数操作flash和OTP存储块时,不要执行任何的中断服务程序。直到API函数完成或者退出flash和OTP,才可以执行或者操作数据空间 3、不能执行API回调函数在flash或者OTP中 4、不要停止擦除、编程当他们执行时 5、在执行擦除、编程时,不要执行代码或者从flash或者OTP数据空间里取数据。  

统计信息

已有1272人来访过

  • 芯币:9430
  • 好友:--
  • 主题:2328
  • 回复:707
  • 课时:--
  • 资源:19

留言

你需要登录后才可以留言 登录 | 注册


博浩元电子 2018-10-25
不错,干货
查看全部