火辣西米秀

  • 2019-05-20
  • 发表了主题帖: MSP430F5529 通用I/O口的设置

    先学习I/O。I/O的操作是最基本的,而且这些寄存器也都要牢牢掌握。MSP430难就难在它有大量的寄存器需要设置,虽然不是每个都必须掌握,但I/O控制寄存器我觉得肯定还要记住的。这个太基础了啊啊-_'   大家手里要常备一份原理图(PCB图),方便查线和配置引脚 一、通用I/O的简单操作(设置) 1.1  I/O的简介         特点:①多种复用和设置(即可控制是否输入、是否输出、是否接上拉电阻、是否接下拉电阻、是否可接受中断);                       ②一般情况下,P1和P2都是具有中断能力的。从P1和P2接口的各个I/O管脚引入的中断可以独立的被使能并且设置为上升沿或者下降沿触发。对应的中断向量表分别为P1IV和P2IV,它们只能进行字操作,并且PAIV这个寄存器根本不存在。                       ③P1、P2可合为PA,P3、P4可合为PB,…PC、PD。所以P1为8位BCD 0x00,PA为16位BCD 0x0000。当进行字操作写入PA口时,所有的16位都被写入这个端口;当利用字节操作写入PA口的低字节时,高字节保持不变;                       ④由于430很多I/O和外围电路接线,所以这里常用位操作。如事先定义(接下来也会用到,先在此声明)BIT0=0X01、BIT1=0X02、BIT3=0X04…BIT7=0X80,那么将P1.1、P1.3的输出设为1的时候,就可以这样操作:P1OUT|=(BIT1+BIT3)。这样显得很清楚。                       ⑤没有用到的I/O,要统一拉低为好。此外,当读入的数据长短小于端口最大长度时,那些没有用到的位会被视零。 1.2   I/O的简单配置                     430I/O的配置是用软件来实现的,是通过相应的配置寄存器来实现的。(用到某个I/O时,一定要先配置好该I/O,否则易出错)       1.2.1  I/O方向设定寄存器PXDIR                    如设定P1.1和P1.2为输出状态,操作为: P1DIR |=(BIT1+BIT2)等价于PADIR |=(BIT1+BIT2)也等价于PADIR_L|=(BIT1+BIT2。     拉高设定为输出,拉低设定为输入(默认)。       1.2.2  I/O输入设定寄存器PXIN                     如设定P1.1和P1.2的输入为低电平,操作为: P1IN &=~(BIT1+BIT2)。       1.2.3      I/O输出设定寄存器PXOUT     ①当只用为简单的输出时:如设定P1.1和P1.2输出高电平,操作为: P1OUT |=(BIT1+BIT2)。                    ②如果该引脚为正常I/O功能,且当前已设定为输入方向,且上拉/下拉电阻寄存器是有效地。那么PXOUT可以用来配置上拉和下拉电阻:                                    低电平为下拉电阻;                                    高电平为上拉电阻;        1.2.4  上拉/下拉电阻使能寄存器PXREN                                    低电平该寄存器为无效状态;                                    高电平该寄存器为有效状态;        1.2.5  输出驱动能力设置寄存器PXDS                             弱化驱动可以减弱电磁干扰EMI,全力驱动会增强电磁干扰。默认为减弱驱动。                             低电平表示减弱的驱动(默认);                             高电平表示全力的驱动;       1.2.6  功能选择寄存器PXSEL                             用来声明该端口是要应用于外围电路的特殊功能(不决定输入输出方向),默认为低电平。                              低电平表示普通的I/O(默认);                              高电平表示该引脚将有连接外围电路的特殊用途;                       如:开发板初始化函数HAL_Board.c中有这样一句程序:                                    P5SEL |=(BIT2+BIT3)(=00001100);                                    这句话的意思就是声明P5.2和P5.3将有特殊用途,实际上这两个I/O接的是外部的高频时钟晶振(之后还要设定为输入状态才可以)。                             此外需要注意的是,一旦某个I/O的PXSEL置高了,那么该引脚将不能再被用为中断引脚。 总结,简单的程序应用: /*实现LED的闪烁*/LED位于每个触摸按键下方,具体接口请查询原理图 #include    <msp430.h>          该头文件内部包含430各个寄存器的配置情况 void main(void) {        WDTCTL=WDTPW+WDTHOLD;                       //关闭看门狗        P1DIR|=(BIT0+BIT1+BIT2+BIT3+BIT4+BIT5);             //P1.0-P1.5方向为输出,BITX的定义在msp430.h中        P1OUT&=~(BIT0+BIT1+BIT2+BIT3+BIT4+BIT5);         //清零        /*P1SEL=0X00;   PXDS=0X00;默认*/        int i=0,j=0;        while(1)        {               if(i>5)                      i=0;               else               {                      switch(i)                      {                      case 0:P1OUT=0x01;break;                      case 1:P1OUT=0x02;break;                      case 2:P1OUT=0x04;break;                      case 3:P1OUT=0x08;break;                      case 4:P1OUT=0x10;break;                      case 5:P1OUT=0x20;break;                      }               }               i++;               for(j=20000;j>0;j--);                               //延时        } }

  • 2019-05-19
  • 发表了主题帖: 有关MSP430单片机UART_FIFO 发送 接受

    一共有2个程序 程序1发送: #include <msp430x42x.h> #define TXBUF_SIZE  32                    /*发送FIFO的最大容量*/ unsigned char TX_BUFF[TXBUF_SIZE];  /*发送FIFO缓冲区数组*/ unsigned int  UART_OutLen=0;        /*发送FIFO内待发出的字节数*/ unsigned int  TX_IndexR=0;          /*发送FIFO内的读指针*/ unsigned int  TX_IndexW=0;          /*发送FIFO内的写指针*/ /***************************************************************** * 名    称:UART0_PutChar() * 功    能:从串口发送1字节数据(向缓冲队列内填入1字节待发送数据) * 入口参数:Chr:待发送的字节 * 出口参数:返回1表示发送成功,             返回0表示发送失败。      * 说    明: 发送过程中,不阻塞CPU运行 *****************************************************************/ char UART0_PutChar(unsigned char Chr) {   if(UART_OutLen == TXBUF_SIZE) //如果FIFO已满   {     return (0);                 // 不发送数据,返回发送失败标志   }   if(UART_OutLen==0)            // 如果是第一个字节   {     IFG1|=UTXIFG0;              // 人为制造第一次中断条件      }   _DINT();                      // 涉及FIFO操作时不允许中断,以免数据错乱   UART_OutLen++;                // 待发送字节数加1   TX_BUFF[TX_IndexW] = Chr;     // 待发送数据通过写指针写入FIFO   if (++TX_IndexW >= TXBUF_SIZE)// 写指针递增,且判断是否下标越界    {     TX_IndexW = 0;              // 如果越界则写指针归零(循环队列)          }   IE1 |= UTXIE0;                  // 允许UART0的发送中断,在中断内依次发送数据      _EINT();                      // FIFO操作完毕,恢复中断允许   return (1);                   // 返回发送成功标志    }                                    #pragma vector=UART0TX_VECTOR __interrupt void UART_TX (void)         // 串口发送中断 {    if(UART_OutLen>0)                    // FIFO内是否有待发送的数据?        {                                           UART_OutLen--;                // 待发送数据字节数减1             U0TXBUF=TX_BUFF[TX_IndexR];   // 从尾指针读取一个字节并发送           if (++TX_IndexR >= TXBUF_SIZE)// 读指针递增,且判断是否下标越界            {                                                 TX_IndexR = 0;             // 如果越界则写指针归零(循环队列)            }        }     else  IE1 &=~ UTXIE0;  // 如果数据已发完,则关闭UART0的发送中断,停止发送   }    /***************************************************************** * 名    称:UART0_PutChar_Legacy() * 功    能:传统的从串口发送1字节数据程序,供对比用 * 入口参数:Chr:待发送的字节    * 说    明: 发送过程中,会阻塞CPU运行 *****************************************************************/ void UART0_PutChar_Legacy(char Chr) {   TXBUF0=Chr; while ((IFG1 & UTXIFG0)==0);         // 等待该字节发完 } void main( void ) {                                                                  WDTCTL = WDTPW + WDTHOLD;           // 停止看门狗   FLL_CTL0 |= XCAP18PF;                // 配置晶振负载电容   U0CTL = CHAR;                        // 异步通讯模式,8位数据,无校验,1位停止位。   ME1 |= UTXE0 + URXE0;                // 开启串口0收发模块   U0TCTL |= SSEL0;                // 选择ACLK作为串口波特率时钟源。   U0BR1 = 0;                        //   U0BR0 = 13;                        // 分频系数整数部分=13   U0MCTL = 0x6B;                // 分频系数小数部分调制=5/8。(2400bps)   P2SEL |= BIT4 + BIT5;  // P2.4,5 开启第二功能,作为串口收发引脚(不同单片机有差别)   _EINT();                        // 总中断允许   while(1)   {     TACTL = TASSEL_2 + MC_2 + TAIE + TACLR; // 用TA测量传统发送程序所需时间                                                        UART0_PutChar_Legacy(0x01);     UART0_PutChar_Legacy(0x02);     UART0_PutChar_Legacy(0x03);     UART0_PutChar_Legacy(0x04);     UART0_PutChar_Legacy(0x05);             //测试,发送8字节数据     UART0_PutChar_Legacy(0x06);     UART0_PutChar_Legacy(0x07);     UART0_PutChar_Legacy(0x08);     TACTL = TASSEL_2 + MC_0;                // TA停止计时     _NOP();                           // 在这一句设断点查看TAR值(29652个周期)     __delay_cycles(1000000);             TACTL = TASSEL_2 + MC_2 + TAIE + TACLR; // 用TA测量带FIFO的发送程序所需时间                                                        UART0_PutChar(0x01);     UART0_PutChar(0x02);     UART0_PutChar(0x03);     UART0_PutChar(0x04);     UART0_PutChar(0x05);                    //测试,发送8字节数据     UART0_PutChar(0x06);     UART0_PutChar(0x07);     UART0_PutChar(0x08);     TACTL = TASSEL_2 + MC_0;                // TA停止计时     _NOP();                           // 在这一句设断点查看TAR值 (440个周期)        __delay_cycles(1000000);                    //约一秒发送一次   } } 复制代码 程序2接受: #include <msp430x42x.h> #define RXBUF_SIZE  32                              /*接收FIFO的最大容量*/ unsigned char RX_BUFF[RXBUF_SIZE];          /*接收FIFO缓冲区数组*/ unsigned int  UART_InpLen=0;                /*接收FIFO内待读取的字节数*/ unsigned int  RX_IndexR=0;                  /*接收FIFO的读指针*/ unsigned int  RX_IndexW=0;                  /*接收FIFO的写指针*/ /***************************************************************** * 名    称:UART0_GetChar() * 功    能:从串口读取1字节数据(从缓冲队列内读取1字节已接收的数据) * 入口参数:*Chr:读取数据所存放的地址指针 * 出口参数:返回1表示读取成功,返回0表示读取失败。      * 说    明: 读取过程中,不阻塞CPU运行 *****************************************************************/ char UART0_GetChar(unsigned char *Chr) {   if(UART_InpLen==0) return(0);         // 如果FIFO内无数据,返回0   _DINT();                              // 涉及FIFO操作时不允许中断,以免指针错乱                                 UART_InpLen--;                        // 待读取数据字节数减1     *Chr=RX_BUFF[RX_IndexR];                   // 从尾指针读取一个字节作为返回值   if (++RX_IndexR >= RXBUF_SIZE)        // 读指针递增,且判断是否下标越界      {                                           RX_IndexR = 0;                   // 如果越界则写指针归零(循环队列)      }   _EINT();                              // FIFO操作完毕,恢复中断允许   return (1);                           // 返回发送成功标志    }                                    /***************************************************************** * 名    称:UART0_GetCharsInRxBuf() * 功    能:获取FIFO内已接收的数据字节数 * 入口参数:无 * 出口参数:待读取的字节数      *****************************************************************/ unsigned int UART0_GetCharsInRxBuf() {   return (UART_InpLen);                 // 返回FIFO内数据的字节数    }                                    /***************************************************************** * 名    称:UART0_ClrRxBuf() * 功    能:清除接收FIFO区 * 入口参数:无 * 出口参数:无      *****************************************************************/ void UART0_ClrRxBuf() {   _DINT();        // 涉及FIFO操作时不允许中断,以免指针错乱   UART_InpLen=0;  // 接收的数据清空   RX_IndexR=0;      RX_IndexW=0;    // 头尾指针复位   _EINT(); } #pragma vector=UART0RX_VECTOR __interrupt void UART0_RX (void)         // 串口接收中断 {   UART_InpLen++;                        // 接收字节计数加1   RX_BUFF[RX_IndexW] =U0RXBUF;             // 串口接收数据通过写指针写入FIFO   if (++RX_IndexW >= RXBUF_SIZE)        // 写指针递增,且判断是否下标越界    {     RX_IndexW = 0;                      // 如果越界则写指针归零(循环队列)          }   } void main( void ) {                                                                  unsigned char RxDataBuff[8];   unsigned char Addr;   unsigned char Func;   int i;   WDTCTL = WDTPW + WDTHOLD;           // 停止看门狗   FLL_CTL0 |= XCAP18PF;                // 配置晶振负载电容   U0CTL = CHAR;                        // 异步通讯模式,8位数据,无校验,1位停止位。   ME1 |= UTXE0 + URXE0;                // 开启串口0收发模块   U0TCTL |= SSEL0;                // 选择ACLK作为串口波特率时钟源。   U0BR1 = 0;                        //   U0BR0 = 13;                        // 分频系数整数部分=13   U0MCTL = 0x6B;                // 分频系数小数部分调制=5/8。(2400bps)   P2SEL |= BIT4 + BIT5;  // P2.4,5 开启第二功能,作为串口收发引脚(不同单片机有差别)   IE1 |= URXIE0;         // 开启UART0的接收中断,在中断内接收数据   _EINT();                        // 总中断允许   while(1)   {       __delay_cycles(1000000);//模拟一个长耗时的程序,使CPU暂时不能读取串口     if(UART0_GetCharsInRxBuf()>=10) //每收到10字节数据     {       UART0_GetChar(&Addr);         //读取第1字节       UART0_GetChar(&Func);         //读取第2字节       for(i=0;i<8;i++) UART0_GetChar(RxDataBuff+i); //依次读取后8字节     }   } }

  • 发表了主题帖: 单片机面试问题收集

    集了一些关于单片机面试的问题,希望能帮助到需要找工作的同学! 单片机的最小系统?内部的主要结构? 答:最小系统:电源、晶振(为系统提供基本的时钟信号)、复位电路;内部结构:ROM/RAM、计时器、中断、I/O串并行口、总线扩展控制。 RAM和ROM的区别? 答:ROM(只读存储器):它的信息一次写入后只能被读出,而不能被操作者修改或者删除。一般用于存放固定的程序或数据表格。但是,“只读”这个概念有时候可以被一些新特性的器件颠覆。 RAM(随机存储器):它就是我们平时说的内存,主要用来存放各种现场的输入/输出数据、中间计算结果,以及与外部存储器交换信息,或者作堆栈(特点:先进后出,后进先出)用。它的存储单元根据具体需要可以读出或者改写。 两者区别:RAM只能用于暂时存放程序与数据。一旦电源关闭或发生断电,RAM中的数据就会丢失。而ROM中的数据在电源关闭或者断电后仍然会保留下来。简而言之:相同点它们都是用来存储数据的,不同点存储数据的方式与数据能不能在二次加工不同 单片机I/O口有什么作用?I/0口的驱动能力?上拉电阻与下拉电阻的作用? 答:I/O口最主要的功能用来与外部器件实现数据信息的交互、速度匹配、数据传送方式和增强单片机的负载能力。它在两者之间扮演桥梁的作用,单片机拥有着串行与并行接口。每个种类的单片机的不同并行口也有着各自不同的功能。 单片机输出低电平时,将允许外部器件,向单片机引脚内灌入电流,这个电流,称为“灌电流”,外部电路称为“灌电流负载”。 单片机输出高电平时,则允许外部器件,从单片机的引脚拉出电流,这个电流,称为“拉电流”,外部电路称为“拉电流负载”。 单片机输出驱动能力的问题:每个单个的引脚,输出低电平的时候,允许外部电路,向引脚灌入的最大电流为 10 mA;每个 8 位的接口(P1、P2 以及 P3),允许向引脚灌入的总电流最大为 15 mA,而 P0 的能力强一些,允许向引脚灌入的最大总电流为26 mA;全部的四个接口所允许的灌电流之和,最大为 71 mA。而当这些引脚“输出高电平”的时候,单片机的“拉电流”能力呢?可以说是太差了,竟然不到 1 mA。 结论就是:单片机输出低电平的时候,驱动能力尚可,而输出高电平的时候,就没有输出电流的能力。 综上所述:灌电流负载,是合理的;而“拉电流负载”和“上拉电阻”会产生很大的无效电流,并且功耗大。 设计单片机的负载电路,应该采用“灌电流负载”的电路形式,以避免无谓的电流消耗。 在数字电路中,只有二种状态,要么是高电平,要么是低电平,在通电初期,这些输出状态是不确定的,为了使电路确定状态,必需使用上拉电阻或下拉电阻,使一个原来不确定电平变高的叫上拉电阻,否则就是下拉电阻,上拉电阻就是从电源上接一只电阻到这个状态口上就可以了,(就是把高的电压加到这个点上去,这个点的电位就高了)下拉电阻的接法,从这个状态口接一只电阻到负极(或数字接地),因电路形式与类别不同,当输入端有信号,这种变化会反应到输出口,从输出口得到了一个状态,本来应该完成任务了,但这会儿输入口已没信号了,可输出端还是这个状态(这个人习惯不好,开门后总是不关门,加一只弹簧,(电阻)让它自己关门,)这时候也要用到上下拉电阻,这里有复位的作用。 常见的时钟电路有哪些?为什么要使用PLL? 答:先了解一下什么是时钟电路? 时钟电路就是产生像时钟一样准确运动的振荡电路,任何工作都按时间顺序。用于产生这个时间的电路就是时钟电路。 组成:晶体振荡器、晶震控制芯片和电容组成。 现在流行的串行时钟电路有:DS1302、DS1307、PCF8485等 它们的特点:接口简单、价格低廉、使用方便。 DS1302:具有涓细电流充电能力的电路,主要特点:采用串行数据传输,可为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。采用普通32.768KHz晶振。 PLL(PhaseLocked Loop):锁相环电路。用来统一整合时脉讯号,使高频器件正常工作。如:内存的存取资料等。PLL用于振荡器中的反馈技术。许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步。一般的晶振由于工艺与成本原因,做不到很高的频率,而在需要高频应用时,有相应的器件VCO,实现转成高频,但不稳定,故利用锁相环路就可以实现稳定且高频的时脉冲讯号。 什么是时脉:指同步电路中时钟的基础频率,它以(若千次周期每秒)来度量,单位是(Hz) 总之:PLL可以同步频率,相位正交。倍频、变频。 单片机的寻址方式有哪些? 答:80C51有七种寻址方式: 1、立即寻址,寻址空间为ROM; 2、直接寻址,寻址空间为片内RAM的低128B和特殊功能寄存器; 3、寄存器寻址,寻址空间为A、B、DPTR、CY、通用工作寄存器等; 4、寄存器间接寻址,片内RAM低128B、片外RAM; 5、相对寻址,寻址空间为ROM; 6、变址寻址,寻址空间为ROM; 7、位寻址,寻址空间为片内RAM低128B的位寻址区的128个位,其字节地址为20H~2FH;以及部分可以位寻址的特殊功能寄存器。 参考:AT89C51单片机能直接认识和执行的机器指令有255条,有7种寻址方式,即立即寻址、直接寻址、寄存器寻址、寄存器间接寻址、变址寻址、相对寻址和位寻址。 1. 直接寻址: 指令中直接给出参与操作的数据的地址,该地址一般用direct表示。 汇编指令:MOV A,direct 该指令的功能是将片内RAM地址direct单元中的内容(参与操作的数据)传送到累加器A中,双字节指令。 2. 立即寻址: 指令中直接给出参与操作的数据,称立即数,用data表示。在汇编语言中,为标明立即数,为data加前缀”#”。立即数可以是8位和16位二进制数,分别用#data和#data16表示。 汇编语言指令:MOV A,#data 该指令将立即数data传送到累加器A中,双字节指令。 3. 寄存器寻址: 参与操作的数据存放在寄存器中,汇编指令中直接以寄存器名来表示参与操作的数据地址,寄存器包括工作寄存器R0~R7、累加器A、AB、数据指针DPTR和位运算寄存器C。 汇编语言指令:MOV A,Rn ;n=0~7 该指令将Rn中的内容传送到累加器A中,单字节指令。 4. 寄存器间接寻址: 寄存器间接寻址为二次寻址,第一次寻址得到寄存器的内容为(Ri)或(DPTR),第二次寻址是将第一次寻址得到的寄存器内容作为地址,并在其中存、取参与操作的数据。汇编语言中,寄存器前缀@是寄存器间接寻址的标志,有@Ri、@DPTR等。 汇编语言指令:MOV A,@Ri ;i=0、1 该指令是将Ri中的内容作为地址,再将该地址中的内容传送到累加器A中,单字节指令。 5. 变址寻址: 间接寻址由两个寄存器提供。若由A、PC提供,在汇编语言指令中寻址地址表示为@A+PC;若由A和DPTR提供,在汇编语言指令中寻址地址为@A+DPTR。其中,PC或DPTR被称为基址寄存器,A被称为变址寄存器,基址与变址相加为16位无符号加法。若变址寄存器A中内容加基址寄存器DPTR(或PC)中内容时,低8位有进位,则该进位直接加到高位,不影响进位标志。因变址寻址指令多用于查表,故常称为查表指令。 汇编语言指令:MOVC A,@A+DPTR 该指令将DPTR中的内容加上A中的内容作为地址,再将该地址中的内容传送到累加器A中,单字节指令。 6. 相对寻址: 相对寻址是以相对寻址指令的下一条指令的程序计数器PC的内容为基值,加上指令机器代码中的“相对地址”,形成新的PC值(要转移的指令地址)的寻址方式。指令机器代码中“相对地址”指的是用一个带符号的8位二进制补码表示的偏移字节数,其取值范围为-128~+127,负数表示向后转移,正数表示向前转移。 若(PC)表示该指令在ROM中的首地址,该指令字节数为2,执行时分两步操作:(PC)←(PC)+2,(PC)←(PC)+相对地址。第一步完成后,PC中的值为该指令的下一条指令的首地址;第二步完成后,PC中的内容(PC)为转移的目标地址。所以,转移的目标地址范围是该相对寻址指令的下一条指令首址加上-128~—+127字节的地址。 汇编语言指令:SJMP rel 汇编语言相对寻址指令中的”rel”往往是一个标号地址,表示ROM中某转移目标地址。汇编软件对该汇编语言指令进行汇编时,自动算出“相对地址”并填入机器代码中,应将”rel”理解为“带有相对意义的转移目标地址”。 Rel=(PC)+相对寻址指令字节数+相对地址 其中,(PC)为该指令所在ROM中的首地址。 7. 位寻址: 参与操作的数据为“位”,而不是字节,是对片内数据存储器RAM和SFR中可位寻址单元的位进行操作的寻址方式。 汇编语言指令:ANL C,bit 该指令将bit(位地址)中的内容(0或1)与C中的内容进行与操作,再将结果传送到PSW中的进位标志C中。 什么是时钟周期?机器周期?指令周期?它们之间的关系? 答:1、时钟周期又叫做振荡周期;单片机内部时钟电路产生(或外部时钟电路送入)的信号周期,单片机的时序信号是以时钟周期信号为基础而形成的,在它的基础上形成了机器周期、指令周期和各种时序信号。定义为时钟脉冲的倒数(可以这样理解:时钟周期就是单片机外接晶振的倒数,例如:12M的晶振,它的时钟周期就是1/12us),是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。 2、计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段,每一个阶段完成一项工作。例如:取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。完成一项基本操作所需要的时间成为机器周期。一般情况,一个机器周期由若干个S周期(状态周期)组成。机器周期是单片机的基本操作周期,每个机器周期包含S1、S2、……S6这6个状态,每个状态包含两拍P1和P2,每个拍为一个时钟周期(振荡周期)。因此,一个机器周期包含12个时钟周期。依次可表示为S1P1、S1P2、S2P1、S2P2……、S6P1、S6P2。 3、指令周期:计算机从取一条指令开始,到执行完该指令所需要的时间称为指令周期。不同的指令,指令长度不同,指令周期也不一样。但指令周期以机器周期为单位,指令不同,所需的机器指令也不同。 51单片机指令根据指令长度与执行周期分为: 1) 单字节单周期指令 2) 单字节双周期指令 3) 双字节单周期指令 4) 双字节双周期指令 5) 三字节双周期指令 6) 一字节四周期指令 总结:时钟周期是最小单位,机器周期需要1个或者多个时钟周期,指令周期需要1个或者多个机器周期;机器周期是指完成一个基本操作的时间。指令周期是CPU的关键指标,指取出并执行一条指令的时间。一般以机器周期为单位,分单指令执行周期、双指令执行周期等。机器周期是完成一个基本操作的时间单元。时钟周期是CPU的晶振的工作频率的倒数。 单片机有哪些接口,各模块有哪些特性及应用环境? 答:接口电路——用于衔接外设与总线,实现存储空间扩展、I/O口线扩展、类型转换(电平转换、串并转换、A/D转换)、功能模块、通信扩展、总线扩展等。 外围设备——工作设备,连接在接口电路上,主要有输出设备和输入设备。 看门狗相关问题: 看门狗的原理? 答:工作原理:在系统运行以后就启动看门狗计数器,此时看门狗就开始自动计时,如果达到一定的时间还不去给它进行清零,看门狗计数器就会溢出从而引起看门狗中断,造成系统的复位。 为什么会溢出呢? 因为看门狗是一个计数器,而计数器位数有限。能够装的数值也就有限(比如8位的最多装256个数,16位的最多装65536个数),从开启看门狗那刻起,它就开始不停的数机器周期,数一个机器周期就计数器加1,加到计数器盛不下了(这就是溢出)就产生一个复位信号,重启系统。 看门狗分为软件看门狗和硬件看门狗,在什么情况下软件看门狗失效? 答:硬件看门狗是利用一个定时器电路,其定时输出连接到电路的复位端,程序在一定时间范围内对定时器清零,因此程序正常工作时,定时器总不能溢出,也就不能产生复位信号。如果程序出现故障,不在定时周期内复位看门狗,就使得看门狗定时器溢出产生复位信号并重启系统。 软件看门狗原理上一样,只是将硬件电路上的定时器用处理器的内部定时器代替,这样可以简化硬件电路设计,但是在可靠性方面不如硬件定时器。 1、系统内部定时器自身发生故障看门狗就会失效(当然可以通过双定时器相互监视,成本高); 2、中断系统故障导致定时器中断失效。 3、整个程序死机。主程序出现异常。 什么时候喂狗?怎么喂狗?喂狗的注意事项? 答:在实际的单片机应用系统中,到底选用哪种型号的看门狗,采用何种喂狗方式和看门狗连接方式以及在编程中喂狗命令应该放在程序中什么位置,这要根据现场干扰源的特点、已采用的干扰措施、单片机硬件资源和软件结构特点以及对单片机系统的可靠性等具体情况而定。 看门狗一般应用到程序的那些位置? 答: 一些重要的程序,必须让它一直跑着;而且还要时时关心它的状态——不能让它出现死锁现象。(当然,如果一个主程序会出现死锁,肯定是设计或者编程上的失误。首要做的事是Debug。)但如果时间紧迫可以用软件看门狗,暂时应急。 喂狗命令放置位置: 定时中断服务子程序中; 主程序中. 复位相关问题: 复位时单片机有什么动作? 答:主要做的就是初始化每个寄存器,包括最重要的PC指针,不包括RAM,然后单片机从复位地址开始执行程序。 必须使RST引脚(9)加上持续两个机器周期(即24个振荡周期)的高电平。单片机就执行复位操作。如果RST持续为高电平,单片机就处于循环复位状态。 单片机有哪些复位源,什么情况下会导致各个复位源 答:6个复位源 TM4C123GH6PM微控制器有6个复位源: (1)上电复位(POR); (2)外部复位输入引脚有效(RST); (3)掉电检测可以用于由以下任一事件引起:① 低于BOR0,触发值是BOR0的 最高电压值;② 低于BOR1,触发值是BOR1的最高 电压值。 (4)软件启动复位(利用软件复位寄存器); (5)违反看门狗复位条件; (6)MOSC故障。 STM8S共有9个复位源: NRST引脚产生的外部复位 上电复位(POR) 掉电复位(BOR) 独立看门狗复位 窗口看门狗复位 软件复位 SWIM复位 非法操作码复位 EMS复位:当一些关键的寄存器被破坏或错误加载时产生的复位 所有的复位源最终都作用于NRST管脚,并在复位过程中保持低电平。复位入口向量在内存映射中位于固定的地址6000h。 复位的两种启动方式? 答:1、上电复位:要求接通电源后,自动实现复位操作。 2、按钮复位:要求接通电源后,单片机自动复位,并且在单片机运行期间,使用开关也可以实现复位。 复位存在哪些问题? 答:采用上电复位:复位信号上电过程中有掉电现象,可能会造成逻辑方面的错误导致相关芯片复位时间不够、二次复位等、从而不能够正常工作起来。原因:产生的复位信号给了几个芯片用,导致这个复位信号的负载压力比较大,驱动能力不够从而往下调电,此现象常常出现在RC复位上。 复位信号上电前有毛刺与复位信号上电时有毛刺,可能会造成逻辑方面的错误导致相关芯片复位时间不够。方案:加大上拉电阻的阻值。 复位电路有哪些?那种比较好? 答:单片机复位电路主要有四种类型: (1)微分型复位电路: (2)积分型复位电路: (3)比较器型复位电路: 比较器型复位电路的基本原理。上电复位时,由于组成了一个RC低通网络,所以比较器的正相输入端的电压比负相端输入电压延迟一定时间.而比较器的负相端网络的时间常数远远小于正相端RC网络的时间常数,因此在正端电压还没有超过负端电压时,比较器输出低电平,经反相器后产生高电平.复位脉冲的宽度主要取决于正常电压上升的速度.由于负端电压放电回路时间常数较大,因此对电源电压的波动不敏感.但是容易产生以下二种不利现象: 1)电源二次开关间隔太短时,复位不可靠: 2)当电源电压中有浪涌现象时,可能在浪涌消失后不能产生复位脉冲。 为此,将改进比较器重定电路,可以利用数字逻辑的方法和比较器配合,设计的比较器重定电路。此电路稍加改进即可作为上电复位和看门狗复位电路共同复位的电路,大大提高了复位的可靠性。 (4)看门狗型复位电路. 看门狗型复位电路主要利用CPU正常工作时,定时复位计数器,使得计数器的值不超过某一设定的值;当CPU不能正常工作时,由于计数器不能被复位,因此其计数会超过某一值,从而产生复位脉冲,使得CPU恢复正常工作状态。此复位电路的可靠性主要取决于软件设计,即将定时向复位电路发出脉冲的程序放在何处是最优的设计。一般设计,将此段程序放在定时器中断服务子程序中。然而,有时这种设计仍然会引起程序走飞或工作不正常。原因主要是:当程序"走飞"发生时,定时器初始化以及开中断之后的话,这种"走飞"情况就有可能不能由Watchdog复位电路校正回来.因为定时器中断一真在产生,即使程序不正常,Watchdog也能被正常复位.为此提出定时器加预设的设计方法.即在初始化时压入堆栈一个地址,在此地址内执行的是一条关中断和一条死循环语句.在所有不被程序代码占用的地址尽可能地用子程序返回指令RET代替.这样,当程序走飞后,其进入陷阱的可能性将大大增加.而一旦进入陷阱,定时器停止工作并且关闭中断,从而使Watchdog复位电路会产生一个复位脉冲将CPU复位.当然这种技术用于实时性较强的控制或处理软件中有一定的困难。 由此可见,四种复位电路中,看门狗型复位电路较其他三中复位电路更加适合。 堆栈相关问题: 堆栈的原理?过程怎么操作? 答:单片机RAM中,常常会指定一个专门的区域来存放某些特别的数据,它遵循先进后出、后进先出的原则。这个RAM区就是堆栈。堆栈是一种数据结构(数据项按序排列:堆,顺序随意。栈,后进先出)。使用一个称作堆栈指针的专用寄存器指示前的操作位置,堆栈指针总是指向栈顶。 操作过程: 1) 堆栈的建立(初始化) 2) 参数入栈(push) 3) 参数出栈(pop)(后进先出) 堆栈应用在什么地方?存在什么问题?怎么解决? 答:在80C51单片机中,堆栈在子程序调用和中断时会把断点地址自动进栈和出栈。进栈和出栈的指令(PUSH、POP)操作可用于保护现场和恢复现场。由于子程序调用和中断都允许嵌套,并可以多级嵌套,而现场的保护也往往使用堆栈,所以一定要注意给堆栈以一定的深度,以免造成堆栈内容的破坏而引起程序执行的“跑飞”。 堆栈指针SP在80C51中存放当前的堆栈栈顶所指存储单元地址的一个8位寄存器。80C51单片机的堆栈是向上生成的,即进栈时SP的内容是增加的;出栈时SP的内容是减少的。 系统复位后。80C51的SP内容为07H。若不重新定义,则以07H为栈底,压栈的内容从08H单元开始存放。但工作寄存器R0~R7有4组,占有内部RAM地址为00H~1FH,位寻址区占有内部RAM地址为20H~2FH。若程序中使用了工作寄存器1~3组或位寻址区,则必须通过软件对SP的内容重新定义,使堆栈区设定在片内数据RAM区中的某一区域内(如30H),堆栈深度不能超过片内RAM空间。 学习堆栈的作用? 答:在片内RAM中,常常要指定一个专门的区域来存放某些特别的数据,它遵循顺序存取和后进先出(LIFO/FILO)的原则,这个RAM区叫堆栈。 1.子程序调用和中断服务时CPU自动将当前PC值压栈保存,返回时自动将PC值弹栈。 2.保护现场/恢复现场 3.数据传输 也就是说,堆栈是进入中断的时候用到的,单片机一旦遇到中断请求,就会去处理中断,处理完后再回来处理主程序,这样就涉及到了一个问题,单片机要保存中断之前的信息,以便处理完后能够回到主程序中,单片机会在响应中断前,把单片机现在的指针地址(也就是处理完要返回的地址),以及一些必要的数据压入堆栈(没有这些数据,单片机处理完中断后就无法确定主程序的状态)。 中断相关问题: 中断的响应过程?优先级? 答:1、响应过程: 1)根据响应的中断源的中断优先级,使相应的优先级状态触发器置1; 2) 执行硬件中断服务子程序调用,并把当前程序计数器PC的内容压入堆栈,保护断点,寻找中断源。 3) 清除相应的中断请求标志位(串行口中断请求标志RI和TI除外); 4) 把被响应的中断源所对应的中断服务程序的入口地址(中断矢量)送入PC,从而转入相应的中断服务程序。 5) 中断返回,程序返回断点处进行执行。 2、优先级:中断的优先级有两个:查询优先级与执行优先级 查询优先级:查询优先级是不可以更改和设置的 1)系统默认的优先级(逻辑上): 外部中断0 > 定时器中断0 > 外部中断1 > 定时器中断1 > 串行中断 2)由IP寄存器来决定优先级。当IP对应位置为1时,该中断级别提高。同为1是按默认级别 中断嵌套的好处与坏处? 答:中断嵌套:CPU在处理级别较低的中断过程中,出现了级别较高的中断请求。CPU停止执行低级别中断,执行高级别的中断处理程序后,再接着执行低级别的未被处理完的中断程序。 使用中断嵌套可以使高优先级别的中断得到及时的响应和处理。 1) CPU与外部设备并行工作 2) 能够处理例外事件 3) 实现实时处理 4) 实现人机联系 5) 实现用户程序与操作系统的联系 6) 实现多道程序并行执行 7) 在多处理机系统中,实现处理机之间的联系 坏处:高级别的中断一直持续的话会影响低级别中断的处理。 中断向量表的理解 答:中断源的识别标志,可用来形成相应的中断服务程序的入口地址或存放中断服务程序的首地址称为中断向量。把所有的中断向量集中起来,按中断类型号从小到大的顺序存放到存储器的某一区域内,这个存放中断向量的存储区叫做中断向量表,即中断服务程序入口地址表。 中断在单片机中起到什么作用? 答:中断能实现快速的CPU与慢速的外设同步工作,实现数据传送、故障检测与处理、人机联系、多机系统、多道程序分时操作、实时信息处理等。 其它: 什么是临界段,哪些情况下会存在临界段的问题,如何进行临界段保护。 答:1.临界段代码,也叫临界区,是指那些必须完整连续运行,不可被打断的代码段。 2. ①读取或者修改变量(特别是用于任务间通信的全局变量)的代码,一般来说这是最常见的临界代码。 ②调用公共函数的代码,特别是不可重入的函数,如果多个任务都访问这个函数,结果是可想而知的。总之,对于临界段要做到执行时间越短越好,否则会影响系统的实时性。 3.中断处理程序和任务都会访问的临界段代码,需要使用关中断的方法加以保护;仅由任务访问的临界段代码,可以通过给调度器上锁的方法来保护。 什么是可重入型函数,使用时需要注意哪些问题 答:可重入函数 不为连续的调用持有静态数据。 不返回指向静态数据的指针;所有数据都由函数的调用者提供。 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。 如果必须访问全局变量,记住利用互斥信号量来保护全局变量。 绝不调用任何不可重入函数。 注意事项: 1 :编写可重入函数时,应注意局部变量的使用(如编写C/C++ 语言的可重入函数时,应使用auto 即缺省态局部变量或寄存器变量) 说明:编写C/C++语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。 2 :编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P 、V 操作)等手段对其加以保护 说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。 专用词汇解释: VCC:C=circuit 表示电路的意思, 即接入电路的电压; VDD:D=device 表示器件的意思, 即器件 内部的工作电压; 对于数字电路来说,VCC是电路的供电电压,VDD是芯片的工作电压(通常Vcc>Vdd),VSS是接地点;有些IC既有VDD引脚又有VCC引脚,说明这种器件自身带有电压转换功能。 VEE:发射极电源电压, Emitter Voltage, 一般用于 ECL 电路的负电源电压. VSS:S=series 表示公共连接的意思,通常是指电路公共接地端电压。 VPP:不同芯片对Vpp的定义稍有不同,比如电压峰峰值,单片机中Vpp多数定义为编程电压 在场效应管(或COMS器件)中,VDD为漏极,VSS为源极,VDD和VSS指的是元件引脚,而不表示供电电压。一般来说VCC=模拟电源,VDD=数字电源,VSS=数字地,VEE=负电源

  • 2019-05-18
  • 发表了主题帖: 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; }

  • 2019-05-16
  • 发表了主题帖: MSP430 FR2xx系列 MCU BSL与MSP432 P系列MCU BSL对比汇总

         引导加载程序 (BSL) 是内置到 MSP 低功耗微控制器 (MCU) 中的应用。借助该应用,用户可以与 MCU 通信,以便从其存储器中读取数据或向其中写入数据。该功能主要用于在原型设计、最终生产和服务期间对器件进行编程。可以根据需要修改可编程存储器(闪存或 FRAM)和数据存储器 (RAM)。不同的 BSL 可提供与之通信的不同外设,例如 UART、I2C、SPI 或 USB。本文将对MSP430 FR2xx系列MCU与MSP432 P系列MCU的BSL进行总结性对比。1   MSP BSL总览https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/0003.aa.png图1 MSP430 FR2xx_4xx与MSP432 BSL总览图1为MSP430 FR2xx_4xx系列MCU与MSP432 P系列BSL的总览。本文将以最新的FRAM系列MCU MSP430FR235x/215x与MSP432P401R为例,对这两款MCU的BSL进行比较。2   MSP430FR2xx与MSP432 P系列BSL比较2.1   BSL 通用功能2.1.1 BSL memory对于MSP430FR235x/215x,其BSL代码的memory存在于ROM中,大小为3kB,前2kB的地址区间为0x1000 - 0x17FF,后1kB地址区间为0xFFC00 - 0xFFFFF。因为该代码存在于ROM中,所以用户不能修改。对于MSP432P401R,其BSL代码的memory存在于Flash中,大小为8kB,地址区间为0x00202000 - 0x00203FFF。对于MSP430FR235x/215x,如果要实现定制化的BSL,则需要占用FRAM中的部分资源来实现,MSP430FR2xxx的客制化BSL源代码及工程文件见:MSP430FRBoot  1_01_00_00。MSP32的BSL代码的起始地址可以通过Flash Mailbox中的BSL Start Address寄存器修改,见图2。Flash中的代码内容也是用户可以自定义修改的,MSP432P401R的BSL源代码及工程文件见:MSPBSL_CustomBSL432  1_01_00_00。https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/bb.png图2 BSL代码起始地址2.1.2 BSL 配置对于MSP432P401R,其BSL外设默认端口引脚可以通过TLV进行配置,具体配置数据如图3,https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/cc.png图3 BSL配置数据同时,MSP432P401R支持用户自定义配置,如BSL功能使能,I2C slave address及BSL invocation引脚及状态配置等,具体配置见图4。这些自定义配置通过Boot override flash mailbox实现。https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/dd.png图4 Boot Override Flash Mailbox2.1.3 BSL串行接口对于MSP430FR235x/215x,BSL串行接口支持UART和I2C接口,对于MSP432P401R,其不仅支持UART和I2C接口,还支持SPI接口。2.2 BSL协议MSP430FR235x/215x和MSP432P401R使用的都是“5xx, 6xx”系列的协议,它们的command基本是相同的,BSL数据包格式如图5,详细数据包格式见BSL用户手册。需要注意的是,由于MSP432P401R的flash更大,其具有32bit的寻址空间,所以它还具有32位地址操作的BSL协议指令。另外,MSP430FR235x/215x的mass erase命令是不需要BSL密码的,而MSP432P401R的mass erase,Sector erase命令都受BSL密码保护。https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/5340.ee.png图5 BSL数据包格式2.3 BSL进入方式MSP430FR235x/215x和MSP432P401R BSL均有3中进入方式:1、空片自动进入BSL;2、软件调用;3、硬件时序进入。2.3.1 空片入口指复位向量为全F时(MSP430FR235x/215x为0xFFFF,MSP432P401R为0xFFFFFFFF),BSL会被Bootcode自动唤醒。复位向量放置的是用户程序的首地址,如果复位向量为全F,则该芯片没有用户程序。Bootcode在执行过程中通过检测复位向量中的内容确定该芯片是否为空片,如果为空片,则唤醒BSL。该功能可用于产品量产时,对芯片批量快速烧写。2.3.2 软件调用对于MSP430FR235x/215x,其BSL程序的入口地址为0x1000,将PC指针指向0x1000即可唤醒BSL。例程代码如下:__disable_interrupt(); // disable interrupts((void (*)())0x1000)(); // jump to BSL对于FRAM系列的MCU,这里有3点额外的注意事项:1、唤醒BSL前需要禁用总中断;2、由于FRAM最高支持8MHz的时钟频率,在唤醒BSL前需要将MCLK的频率设定为小于等于8MHz;3、BSL默认使用的定时器Timer模块如果在应用程序中被调用,且没有复位就进入BSL唤醒程序,这样就会影响BSL程序的执行,所以需要在BSL唤醒前对该Timer进行复位。对于MSP432P401R,其BSL程序的入口地址默认为0x00202000,同时其入口函数支持参数配置。需要注意的是,MSP432调用BSL程序前,也需要禁用总中断。例程代码如下:#define BSL_PARAM 0xFC48FFFF // I2C slave address = 0x48, Interface selection = Auto#define BSL_API_TABLE_ADDR 0x00202000 // Address of BSL API table#define BSL_ENTRY_FUNCTION (*((uint32_t *)BSL_API_TABLE_ADDR))MAP_Interrupt_disableMaster();NVIC->ICER[0] = 0xFFFF;NVIC->ICPR[0] = 0xFFFF;NVIC->ICER[1] = 0xFFFF;NVIC->ICPR[1] = 0xFFFF;((void (*)())BSL_ENTRY_FUNCTION)((uint32_t)BSL_PARAM); // Call the BSL with given BSL parameters2.3.3硬件时序进入对于MSP430FR235x/215x,其通过RESET和TEST脚的固定时序唤醒BSL,如图6。https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/7416.ff.png图6 硬件时序唤醒BSL对于MSP432P401R,其可以通过启动阶段用户指定的IO的电平状态来唤醒BSL。需要用户提前对mailbox进行配置。以P1.0脚高电平唤醒为例,其时序图如图7。https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/0844.gg.png图7 GPIO输入时序唤醒BSL2.4 BSL工具支持2.4.1硬件工具BSL的官方硬件工具有MSP-BSL ‘Rocket’和MSP-FET,其主要是给MSP430FR235x/215x在非空片状态下提供RST脚和TEST脚的硬件时序,来唤醒BSL。对于空片BSL唤醒,软件BSL唤醒及MSP432的硬件输入顺序唤醒来说,其只需要一个UART就可以实现BSL下载。2.4.2 软件工具BSL的官方软件工具为BSL Scripter,其将用户的命令及TXT格式的用户代码按照BSL协议的封包格式通过串行接口发送给芯片,或者将芯片中memory中的数据读回。脚本文件中通过指令MODE FRxx或者MODE P4xx区分MSP430 FRAM系列或者MSP432系列的MCU,其余命令部分可以通用。2.5 BSL安全性2.5.1 BSL密码对于MSP430FR235x/215x,其提供32 Bytes的BSL密码保护,即当BSL密码正确时,才会执行密码保护的BSL命令。密码所在地址为FRAM中0xFFE0到0xFFFF的地址,该地址区域也是硬件中断的地址区域。一般情况下,编译器会将中断服务程序的首地址存放在中断向量表中,而没有用到的中断向量,编译器一般也会自动映射到硬件错误函数。所以,用户在手动修改BSL密码时,需要注意不能修改程序中已经使用到的中断向量地址中的内容。对于MSP432P401R,其提供256 Bytes的BSL密码保护,密码所在地址为Flash中0x00到0x40的地址,该地址默认也是中断向量的地址。用户在手动修改时也需要注意和MSP430同样的问题。2.5.2 密码错误时执行Mass eraseMSP430和MSP432均具有当BSL密码错误时执行Mass erase的功能。该功能增加了用户调试的方便性,只需要发送一次错误密码,就能够让芯片自动擦除memory,下次执行BSL命令只需要发送默认密码(全F)即可。在MSP430 FRAM系列MCU中,该功能的使能和禁用通过BSL signature配置,FRAM中地址区间为FF84h - FF87h。当数据为全5时,BSL功能被禁用;当为全A时,BSL受密码保护,密码错误时的Mass erase功能被禁用,用户在量产时可以使能该功能;当为其他的值时,密码错误时的Mass erase功能被使能。https://e2echina.ti.com/resized-image/__size/1230x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/6283.hh.png图8 MSP430 BSL SignatureMSP432中,该功能是由BSL代码实现的,如果要禁用该功能,需要用户修改BSL源代码。另外补充一点,MSP432 BSL支持payload加密功能。ReferenceMSP430 FRAM Device Bootloader (BSL) User's Guide (Rev. P)MSP432P4xx SimpleLink™ Microcontrollers Bootloader (BSL) User's Guide (Rev. G)Bootloader (BSL) Scripter User's Guide (Rev. F)MSP430FR235x, MSP430FR215x Mixed-Signal Microcontrollers datasheet (Rev. B)MSP430FR4xx and MSP430FR2xx Family User's Guide (Rev. H)MSP432P401R, MSP432P401M SimpleLink™ Mixed-Signal Microcontrollers datasheet (Rev. G)MSP432P4xx SimpleLink™ Microcontrollers Technical Reference Manual (Rev. H)

  • 发表了主题帖: C语言里面捕获错误机制

    在C语言中异常处理一般有这么几种方式: 1.使用标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于<stdlib.h>头文件中。 2.使用assert(断言)宏调用,位于头文件<assert.h>中,当程序出错时,就会引发一个abort()。 3.使用errno全局变量,由C运行时库函数提供,位于头文件<errno.h>中。 4.使用goto语句,当出错时跳转。 5.使用setjmp,longjmp进行异常处理。 这里给你一个exit方法的实例 1 #include <stdio.h> 2 #include <stdlib.h> 3 double diva(double num1,double num2)         //两数相除函数 4 { 5     double re; 6     re=num1/num2; 7     return re; 8 } 9 int main() 10 { 11    double a,b,result; 12  printf("请输入第一个数字:"); 13   scanf("%lf",&a); 14   printf("请输入第二个数字:"); 15   scanf("%lf",&b); 16   if(0==b)                                //如果除数为0终止程序 17   exit(EXIT_FAILURE); 18 result=diva(a,b); 19    printf("相除的结果是: %.2lf\n",result);    20 return 0; 21 }

  • 2019-05-15
  • 发表了主题帖: MSP430的机器人定位系统电路设计

          对于在室外环境工作的移动机器人通常使用惯导/卫星组合导航方式。惯性导航系统具有完全自主、抗干扰强、隐蔽能力好和输出参数全面等优点,但它的鲁棒性极低,误差会不断随时间累积发散。卫星导航系统具有精度高、定位范围广和误差不随时间累积等优点,但其自主性差、易受外界遮挡和干扰、接收机数据更新频率低等缺点。因此工程上常常将两者互补结合使用,组成卫星/惯性组合导航系统。   本文以低功耗MSP430F149为核心,设计了能够同时实现卫星导航(GNSS)接收机、惯性测量单元(IMU)、气压高度等导航信息的高速采集与高速合路传输,并进行初步导航定位信息融合的导航系统,即可为室外移动机器人提供直接的导航服务,也可作为高精度组合导航系统的原始测量信息高速采集系统。系统设计的关键是利用单片机有限的接口资源实现了多传感器信息并行采集,设计了有效的数据同步方法,解决了气压传感器数据手册疏漏导致的无法接入问题,给出了机器人组合定位的基本方法。系统充分利用了MSP430F149单片机的能力,具有结构简单、低功耗、对传感器具有普适性等优点。   本系统由电源、气压计接口、IMU接口、GNSS接收机接口、SPI转UART模块及MSP430F149构成。系统组成如图1所示。组合导航系统的功能实现分为IMU数据接收与解析、GNSS数据接收与解析、气压计数据接收与解析、组合导航解算以及数据输出五个部分。IMU数据接收与解析功能用来获取导航解算中需要的加速度和角速度信息;GNSS数据接收与解析功能用来获取导航解算中需要的位置和速度信息(松耦合组合)或者 GNSS伪距和伪距率(紧耦合组合);气压计数据接收与解析功能用来获取高度信息;组合导航解算功能为系统核心,用来进行组合导航解算;数据的输出包括原始数据包的整合输出和解算结果的输出。   本文所使用的惯性器件和GNSS接收机都是RS-232电平的UART接口,具有通用性,用户可根据成本考虑不同精度的设备。气压计选用美国MEAS公司生产的MS5803-02BA,已经固化在电路中。http://www.dzsc.com/data/uploadfile/201551114519271.jpg 图1 系统组成结构图  微控制器接口   整个组合导航定位系统需要三个UART接口和两个SPI接口。其中两个UART接口由430单片机自带的UART资源提供,另外一个UART接口由 GPIO模拟SPI通过MAX3111E芯片转化得到;两个SPI接口由GPIO模拟得到。另外需要一个外部中断引脚捕获秒脉冲信号(PPS)、一个外部中断引脚捕获MAX3111E中断信号。MSP430F149管脚资源分配如表1所示。   电源电路   本系统供电需求为3.3V供电,因此采用AMS1117稳压芯片,接入5V电源即可输出3.3V稳定电压,可提供1A电流,满足系统供电需求。电路设计如图2所示。http://www.dzsc.com/data/uploadfile/201551114519594.jpg   IMU器件及GNSS接收机接口电路   IMU器件及GNSS接收机都采用UART接口方式接入,采用RS232协议。因此可使用430单片机上自带的两个UART接口,但是需要进行TTL电平与RS232电平转换。这里采用常见的MAX3232芯片,电路设计如图3所示。http://www.dzsc.com/data/uploadfile/201551114519508.jpg图3 IMU及GNSS接口电路  气压计MS5803-02BA接口电路   MS5803-02BA[3]是由MEAS公司生产的数字压力传感器,分辨率达10cm。芯片内部包含一个高线性的压力传感器和一个内部工厂标定系数的超低功耗24位ΔΣ型ADC。该款芯片有SPI和I2C两种接口方式,通过芯片的PS引脚配置了选择不同的接口方式(PS置低时,采用SPI工作模式;PS置高时,采用I2C工作模式)。本文所阐述的定位系统将气压计配置为SPI工作模式。MS5803-02BA与微控制器间的接口电路设计如图4所示。http://www.dzsc.com/data/uploadfile/201551114519863.jpg                                                       图4 MS5803-02BA接口电路   MS5803-02BA的控制命令包括复位命令、温度ADC命令、气压ADC命令、ADC读取命令、PROM读取命令。控制命令如表2所示。控制命令通过SDI口移位输入,响应结果从SDO移位输出。输入的电平判定在时钟信号的上升沿,输出的电平判定在时钟信号的下降沿。输出的气压值可以进行温度补偿,需要利用芯片内部PROM中的系数来补偿。ADC读取命令输入之后,输出24位ADC结果;PROM读取命令输入之后,输出16位补偿系数。   本文基于MSP430F149单片机设计的室外移动机器人组合导航定位系统,通过接口的扩展使得该款定位系统能够接入IMU、GNSS接收机、气压计三路信息,完成初步导航定位服务功能,同时可作为多路数据采集设备,将多路数据整合到一路高速输出接口,用于进一步的高精度导航解算。该系统根据使用者的需求不同,可接入不同成本和精度的设备,只要满足RS-232协议即可。笔者将其实际运用,整个系统充分利用该款单片机的资源,结构简单、功耗低、适用范围广,不仅可作为初步导航定位服务的设备,还可作为多路数据采集设备。

  • 发表了主题帖: MSP430™ FRAM微控制器实现能量采集

    本帖最后由 火辣西米秀 于 2019-5-15 19:24 编辑       对于很多人来说,第一次接触能量采集可能是在早期使用太阳能便携式计算器的时候,虽然如今这种类型的计算器已不再是主流,但是它所使用的技术和理念仍然应用于我们的日常生活中。目前,我们在许多的应用中都能看到能量采集的身影,例如传感器节点、风力涡轮机和室内供能应用等。不过,即使对于这项技术的讨论较之前已经有了很大的发展,当涉及到能量采集时,开发人员仍然面临着与数十年前一样的挑战。       为了在不带来负面影响的情况下产生出所需的能量,通常需要一块物理尺寸很大的太阳能板和一套巨大的热能采集装置,或者是通过设备发出不同频率范围的振动来获得能量,而一切都是由所使用的系统决定。因此,在很多情况下,这个系统的成本甚至会超过取代传统电源所带来的优势。当然,如果由于某些因素必须忽略这些限制的话,也会有例外的情况。例如,在电力线无法到达的偏远地区,风能或太阳能采集可以为电池供电系统提供一个可行的替代能源,尽管这种方法的初始成本会比较高。下面让我们来看一看目前能量采集解决方案所面临的几个重大挑战。https://e2echina.ti.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/5722.1.png图1—经简化的通用框图        首先,通过上方的简化通用框图可以看到,这个系统由输入和输出组成,其中包括传感器、按钮、LED、显示屏、发声器以及目前越来越普遍的无线连通性。这个典型物联网(IoT)架构的边缘节点可以通过Wi-Fi®、Bluetooth®、NFC / RFID或是其它专有接口进行通信。这些无线连接所需的电能低至数uA,而最高也只需要几十或几百mA,在数十毫秒的时间内即可为相关的RF IC和子系统供电。 https://e2echina.ti.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-00-74/0312.2.png图2—常见RF电能使用量图表在很多应用中,设计人员希望将传感器或其它数据存储在非易失性存储器中,因为即使在电力中断时,也可以恢复采集到的数据。所以,诸如EEPROM或FLASH等现存的通用存储器技术在这些能量受限的情况下并不总是最佳选择。       幸运的是,技术的发展方向正让能量采集系统变得可行。其中一项技术集成就是TI的铁电随机访问存储器或FRAM微控制器(MCU)系列。FRAM技术将SRAM存储器的很多优势结合在一起,而同时又具有FLASH存储器的非易失性。一个关键优势就是超低功耗非易失性FRAM的写入,与FLASH不同的是,这些写入无需预擦除周期,从而节省了时间和电能。另外一个优势就是FRAM单元的固有低压写入,传统闪存或EEPROM技术需要一个集成电荷泵来完成预擦除周期,而这通常需要5-10mA的电流,运行时间则需要数百毫秒,在需要频繁非易失性写入的应用中,这个额外的能耗会消耗可观的电池电量或采集得来的能量。      购买一次性电池的开销虽然不是很高,不过它们所造成的影响极其深远。全球每年新电池的销售量在数十亿节,而其中只有一小部分是可回收的,这就产生了大量的填埋垃圾。一次性电池的另外一个缺点就是,无论是电池本身或是整个系统,都需要在某些情况下的某个时刻进行替换,而这就产生了潜在的挑战。试想一下,如果电池被安装在部署于海洋底部或山顶的系统中,我们应该如何替换呢?事实上,电池更换的开销可能非常巨大。虽然可充电电池能够减少替换电池的数量,不过,就可充电电池本身而言,它们不一定会能够解决电池更换所带来的所有挑战。当我们使用能量采集对可充电电池进行充电时,这些电池的确能够带来利益。目前,太阳能、热能、运动能(振动或其它动力学效应)和RF等能源已经被广泛接受。其它能源也正处于开发过程中,例如有可能从人类血液中发生的电化学反应采集能量,或者是从植物和树木内部的此类反应中采集能量。       在理想情况下,这些能源将是绵延不断的,不过实际情况并非如此。以太阳能采集装置为例,飘动的云彩也许会遮住太阳光,而室内设施的灯光也不可能永远开启。基于振动的采集装置通常在一个共振频率附近运行,从而限制它们的运行范围,而热能采集装置会在无法保持合适的温差时损失效率或完全停止运行。归根结底,我们不能依赖这个能源保持连续的7x24小时运行,所以我们需要冗余结构。在某些情况下,这个结构有可能是第二采集能源或是一节可充电电池。即使是太阳能计算器都包含一块CR2025电池,以便在办公室光线较暗时作为太阳能的备用能源。      处理电力损耗成为一名能量采集节点设计人员的主要考虑因素。现代微控制器在通电时通过启动顺序运行,这往往需要几毫秒并且会消耗宝贵的电能。如果电力中断,大多数微控制器需要重启,并且在每次电力恢复时运行这个启动代码。      FRAM存储器本身是Compute Through Power Loss(CTPL)这款高度创新型软件实用工具的使能器件。我们甚至可以将CTPL看成是一种非易失性中断处理例程,在这个例程中,当检测到电力损耗时(通常使用一个比较器或ADC输入),关键参数和微控制器状态就被保存至非易失性存储器(NVM)。在出现电力中断的情况下,FRAM所具有的优势将显露无疑,因为设计人员可以直接从中断的位置继续工作而不是从头开始。凭借128KB FRAM MSP430™ 微控制器的低成本MSP430FR6989 MCU Launchpad™ 开发套件能够进行简单演示。      通过将FRAM技术、Compute Through Power Loss代码和Energy Harvesting BoosterPack™ 插入式模块组合在一起,我们为很多能量采集传感器节点打下了良好基础。bq25570所提供的电源备妥信号可以作为Compute Through Power Loss激活的触发信号,从而在电力中断后节省了时间和宝贵的电能。

  • 2019-05-14
  • 发表了主题帖: MSP430,STM8L,cortex-M0内核远程固件升级总结

          在做DLMS协议的仪表时,由于老外要求具有远程固件升级功能,由于第一次做,所以寻求过厂家的帮助,不过流程过于复杂,做产品要考虑可靠。有过这次经验后,所以在其他产品上增加了远程固件升级功能。虽然MSP430自带BSL,         STM8L自带BOOTLOAD,但是过于限制,需要预留接口,并且协议固定,如果使用自己的固件,那么我们可以使用很多介质去传输,比如RS485,MBUS,红外,CPU卡等。结合论坛上各个大神的经验,及自己开发中的经验,在此和大家分享学习。在实际测试过程中,成功率99%以上。不管是MSP430,还是STM8L或者CM0内核,万变不离其宗,只要知道原理和方法,流程都是一样的。 第一篇 在项目开发中,至关重要的是保证产品运行的可靠,如果遇到异常,能否恢复很重要,而不是像砖头一样,程序死在某个地方。固件升级的原理就是重写向量表,在引导区更新app区的flash,然后跳转app区。实际开发中就会有以下问题: 1.如果MCU复位,比如POR,PDR,WDT等复位,都会使sp指针指向复位地址。那么MCU从引导区执行,如果APP区程序有效,应该如何控制程序跳转到APP区。 2.如果APP区或者引导区接受新固件,在更新APP区flash时,如果此时MCU发生掉电,当再次上电后,MCU该如何执行。或许有人说,我们有外部的EEP或者外部的FLASH,会使用状态和标志去记录当时MCU操作flash的状态,当然这些状态和标志有校验,并且存储到外部EEP或FLASH。上电后我们会判断校验,然后读出来作为依据。在理想情况下,这样做非常完美,但是MCU在运行中,什么情况都可能发生。比如电源掉的很快,那么算出来的校验有什么意义,还怎么保证写到EEP或FLASH的可靠性,特别是有外部FLASH,几ma的 电流MCU瞬时根本扛不住。即使是EEP,就算将引导区配置成最低功耗,这种意外也是不可避免的,此时的标志和状态只是徒劳。那么会造成一种MCU假死状态,滞留在引导区,然后死循环。如果要解除,只能通过仿真器进入仿真模式,更改变量值去解除。而这样的后果就违背了升级的初衷和产品的可靠。 3.对于新固件的更新,是接收全部数据再更新还是接收部分数据更新FLASH,这个具体依据自己使用的硬件资源,不过重点还是在于第二点的处理。 4.如果升级过程中,传输数据或读取数据突然中断,或者新的固件验证失败,那么这些操作该如何恢复,而不至于MCU假死。 自己实践中的处理,总结了如下几条: 1.首先我们要明白MCU复位后是要从复位执行,并且MCU中断后,会跳转到实际中断向量地址,也就是向量区重写。在应用区如果有中断发生,MCU会跳转到中断原始地址,通过跳转指令执行位于应用区实际的中断处理函数。例如我使用的是MSP430的FR6972,它的FRAM分配是0x4400-0x13FFF,它的向量区地址在0xFF80-0xFFFF。假如分成两个区,引导区0xF000-0xFFFF,APP区0x7C00-0xEFFF。现在程序执行在0x7C00-0xEFFF的应用区,此时MCU响应了一个中断,假设这个中断函数的入口地址是0xEFF2,按照常理,MCU也应该执行这个地址的内容,实际上,MCU会跳转到这个中断的原始中断向量地址0xFFF2,因为0xEF80-0xEFFF只是我们虚拟的中断向量地址,0xFF80-0xFFFF才是真正的中断向量区。这也是为什么要在引导区重写中断向量,如 #pragma vector=WDT_VECTOR __interrupt void WDT_ISR(void)                         //0xFFF2 { asm(" br &0xEFF2;"); } 执行中断,栈会保存sp等寄存器的内容,执行完后会恢复,继续执行APP区程序。 2.不管是引导区和APP区,MCU的寄存器地址都是固定的,ram的地址也是一样的,但是FLASH是各自独立的,不能重叠。特别注意的是,在引导区和APP区处理全局变量或静态变量时,一定要初始化,或者依据校验从存储器恢复,因为跳转(非中断跳转)会导致这些变量是乱的。 3.要明白编译出的文件格式,知道数据要写到MCU中FLASH的地址。例如MSP430编译出的文件: @F000 01 02 03 04 05 06 07 @F008 31 40 00 24 8C 00 08 1C 3E 40 17 02 3F 4000 00 B0 13 B4 FE 8C 00 00 1C 8D 00 00 F0 3E 4007 00 …… @FFC6 38 F0 3E F0 44 F0 4A F0 50 F0 56 F0 5C F062 F0 68 F0 6E F0 74 F0 7A F0 80 F0 86 F0 8C F092 F0 98 F0 9E F0 A4 F0 AA F0 B0 F0 @FFF2 B6 F0 BC F0 C2 F0 C8 F0 CE F0 D4 F0 08 F0 q @后的内容是代码段的地址,是说明段数据要写入的地址,这些地址不需要写入到FLASH中。地址的分配与link文件分配有关。 -Z(CONST)DATA16_C,DATA16_ID,TLS16_ID,DIFUNCT,CHECKSUM=F000-FF7F -Z(CONST)DATA20_C,DATA20_ID,CODE_ID=F000-FF7F -Z(CODE)CSTART,ISR_CODE,CODE16=F000-FF7F -P(CODE)CODE=F000-FF7F -Z(CONST)SIGNATURE=FF80-FF8F -Z(CONST)JTAGSIGNATURE=FF80-FF83 -Z(CONST)BSLSIGNATURE=FF84-FF87 -Z(CONST)IPESIGNATURE=FF88-FF8F -Z(CODE)INTVEC=FF90-FFFF -Z(CODE)RESET=FFFE-FFFF 像-z,-p这些都是编译指令,(data)(const)(code)都是说明修饰,DATA16_C,DATA16_ID等都是数据段类型描述。 q就是结束标志。 例如stm8l编译出的我文件格式: :108000008200FBA0820166548200FE8D82016655CB    //起始地址是0x8000 :108010008200FEA78200FEA8820126B18200F1C77D     //起始地址是0x8010 :108020008200FEA98200FEAA820113AA8200F38ABE    //起始地址是0x8020 :108030008200F5C68200F6EB8200FEAB8200EFD82C    //起始地址是0x8030 :108040008200F5F982014AE38200FEAC8200FEADB7    //起始地址是0x8040 :108050008200FEAE820136958200FEAF8201164399    //起始地址是0x8050 :108060008200FEB082012E8B8200FEB18200F24BB4    //起始地址是0x8060 :108070008200FEB28200FEB38200FEB48200FEB532    //起始地址是0x8070 …… :108610008D011DBB3D002608BE042602BE0626E9CC :0F862000BE042602BE06260435020000B60087FF :10862F00AE013CBF00905FAE01648D00FC0E725F27 :10863F00016435820165725F016635020167350895 …… 我们可以很容易百度hex文件格式说明,Hex文件是可以烧录到MCU中,被MCU执行的一种文件格式。整个文件以行为单位,每行以冒号开头,内容全部为16进制码。例如”:1000080080318B1E0828092820280B1D0C280D2854”。 第一个字节0x10表示本行数据的长度,“80318B1E0828092820280B1D0C280D28”。 第二,三个字节0x00,0x08表示本行数据的起始地址。 第四个字节0x00表示数据类型,数据类型说明: '00' Data Rrecord:用来记录数据,HEX文件的大部分记录都是数据记录 '01' End of File Record: 用来标识文件结束,放在文件的最后,标识HEX文件的结尾 '02' Extended Segment Address Record: 用来标识扩展段地址的记录 '03' Start Segment Address Record:开始段地址记录 '04' Extended Linear Address Record: 用来标识扩展线性地址的记录 '05' Start Linear Address Record:开始线性地址记录 理解了这些文件的内容,我们就知道了向量区需要些的内容,这点很重要。同时我们可以根据自己的通信协议进行扩展,重新转换这些内容,传输到MCU中进行固件升级。 4.原始的中断向量最好与引导区在一个区域,在引导区执行,最好关闭中断响应,通过查询的标志位的方式来处理。引导区的作用就是实现APP区的FLASH更新,中断的跳转。如果将原始向量区分配到APP区,会导致需要很大的外部存储空间接受新的固件,而且程序的设计也会头重脚轻,不建议使用。 5.如何保证固件更新的成功率,和解决擦写FLASH出现的异常,最好的操作就是先擦写APP区的向量区内容,更新完APP区FLASH,最后写复位向量内容。这样可以省去很多的判断流程,即使中途更新失败,或者掉电,MCU上电后也可以继续更新固件,并且出错率很低。像MSP430,烧写FLASH或者仿真下载,如果烧写时选择默认Memmory option,那么FLASH默认的值是0xFF。当知道分配的APP区的起始地址后,就判断复位向量值是否为0xFF,如果是则说明APP区没有内容,则不跳转,接受或更新固件。如果不是,一般情况下APP区是有内容的,则执行跳转。 6.在实现固件升级时,最好测试指针类型长度,因为选择的数据模式不同,访问不到0x10000以上的地址。比如MSP430的,如果选择mid,所有的指针类型长度占2byte,这样能访问的最大地址是0xFFFF,只有选择large,才可以访问0x10000以上的地址,但是相应的代码面会增加,因为函数列表的内容就扩大了一倍多,还有其他指针操作。Stm8l区分的就是near和far,就052r8分好几种大小的flash,可以在stm8l15x.h中更改宏。还有就是对内存的操作,需要注意对齐补齐,特别是32位机,像cortex-0内核。比如我经常使用 #define M8(adr)          (*((FAR uint8_t* )(adr))) #define M16(adr)         (*((FAR uint16_t* )(adr))) #define M32(adr)         (*((FAR uint32_t* )(adr))) FAR只是一种修饰。可以省略,也可以使用volitale。 7.合并引导区和APP区文件,然后通过烧程器烧录完整文件。关键就是只保留一个结束标志。 第二篇 以下我会通过实例代码来说明,如果有不足,请大家提出建议。 这是stm8L引导区的main()函数。 void main() { //禁止中断使能 disableInterrupts(); Clk_Config(); IO_Config(); LCD_Config(); //解锁FLASH操作 FLASH->PUKR = FLASH_RASS_KEY1; FLASH->PUKR = FLASH_RASS_KEY2; //从存储区读出数据 FLASH_ByteRead(start_EEPROM1ADDR,(uint8_t*)&ImagePara,sizeof(Image)); //使用CRC校验,返回0没错误 if(TestAdjust((uint8_t *)&ImagePara,sizeof(Image))==0) jumpflasg=1; //0x01初始化状态,判断口状态,因为我使用的CPU卡 if((jumpflasg==1)&&(ImagePara.CurState==0x01)&&((GPIOC->IDR&0x02)==0)) {    //擦除APP区向量 FLASHEraseBlock(APPST_BLOCK_NUM,FLASH_MemType_Program); //擦除整个APP区    FlashErase(APPST_BLOCK_NUM,0xEF80); } //如果向量地址有效,跳转引导区 if(ResetVectorValid()==1) {    asm("LDW X,   SP");    asm("LD  A,   $FF");    asm("LD  XL,  A");    asm("LDW SP,  X");    asm("JPF      $9000"); } //显示boot {    LCD->RAM[3]=0x40;    LCD->RAM[4]=0x10;    LCD->RAM[7]=0xFC;    LCD->RAM[8]=0x01;    LCD->RAM[10]=0xC0;    LCD->RAM[11]=0x3F; } while(1) {    //禁止中断 disableInterrupts(); //实时判校验    if(TestAdjust((uint8_t *)&ImagePara,sizeof(Image)))      FLASH_ByteRead(start_EEPROM1ADDR,(uint8_t*)&ImagePara,sizeof(Image));    //如果有卡标志    if((GPIOC->IDR&0x02)==0) {   //如果读卡没错误      if(carderr!=1)      {        Delayms(10);        //读卡第一个块内容,包含我的块个数和校验和        carderr=Read_ImageInfo();        if(carderr!=1)        {              //更新flash,按块操作               while((++ImagePara.Block_dyn)<=ImagePara.Block_static)          {             //读CPU卡内容,更新flash            if(Read_Image(ImagePara.Block_dyn)==FALSE)             {               carderr=1;               break;             }          }          if(carderr!=1)          {             //最后写入向量区             if(Get_Vector()==FALSE)             {               carderr=1;             }          }          if(carderr!=1)          {             //验证块的状态             if(Verify_Image()==FALSE)             {               //校验失败               ImagePara.CurState=0x02;             }             else             {               //校验成功               ImagePara.CurState=0x03;             }                      //算校验,更新数据             adjust_write((uint8_t*)&ImagePara,start_EEPROM1ADDR,sizeof(Image));          }        }      }    }    else {      if(ImagePara.CurState==0x02)      {        memset((uint8_t *)&ImagePara,0,sizeof(Image));        ImagePara.CurState=0x01;        adjust_write((uint8_t *)&ImagePara,start_EEPROM1ADDR,sizeof(Image));      }      carderr=0;    }    if(carderr==1)      LCD->RAM[12]=0x40;    else      LCD->RAM[12]=0x00;    if(ImagePara.CurState==0x03)    {       asm("LDW X,   SP");       asm("LD  A,   $FF");       asm("LD  XL,  A");       asm("LDW SP,  X");       asm("JPF      $9000");    } } } 这是MSP430引导区main()函数 void main() { WDTCTL = WDTPW | WDTHOLD; Init_cmu(); Init_uart_Mbus(); Init_uart_HW(); Test_Image(); if(TestAdjust((uint8*)&ImagPara,sizeof(Image))==0)    jumpflag=1; if((jumpflag==1)&&(ImagPara.curstate==0x04))    Flash_Erase(APP_VECSTARTADDR,APP_VECLEN); if(ResetVectorValid()==1)    asm("mov &0xEFFE,PC;"); while(1)   {    __disable_interrupt();    Test_Image();    CommPara_Mbus.chbuff=&Buff_MBUS[0];    CommPara_HW.chbuff=&Buff_HW[0];    if(UCA1IFG & UCRXIFG)     {      UCA1IFG &= ~UCRXIFG;      StreamHandler_putChar((CommData*)&CommPara_Mbus,UCA1RXBUF);     }    if(UCA0IFG & UCRXIFG)     {      UCA0IFG &= ~UCRXIFG;      StreamHandler_putChar((CommData*)&CommPara_HW,UCA0RXBUF);     }    ApplayerHandler();     if(ImagPara.curstate==0x04)     {      Flash_Erase(APP_VECSTARTADDR,APP_VECLEN);      Flash_Erase(APP_CODESTARTADDR,APPSIZE);      EEP_TO_FRAM();      if(Flash_check()==0)      {        ImagPara.curstate=0x05;      }      else      {        ImagPara.curstate=0x06;      }      adjust_write((uint8*)&ImagPara,IMAGE_ADDR,sizeof(Image));     }    if(ImagPara.curstate==0x06)     {      asm("mov &0xEFFE,PC;");     }   } } 这是一个比较早的版本,cortex-0内核的, int main(void) {          Delay_FreeDog();          Poweroff_IO();                                                                                                                gpio config();          init_all();                                                                                                                                              system init();          while(1)          {                    FreeDog();                    if(Flash_Updata_sta()==TRUE)                    {                             DLMS_ImageTypeImageparas,ImageparasA,ImageparasB;                             uint8_tflag=0xff;                             timecout=SecondCounMax;                                                  while(!DiaoDianSTATE()==0)                             {                                      FreeDog();                                    `                               __disable_irq();                                      Uart_revANDsend(HT_UART4,RS485_1CHL);                                      Uart_revANDsend(HT_UART0,HONGW_CHL);                                      Uart_revANDsend(HT_UART2,MODULE_CHL);                                      DLMS_dataHandler(RS485_1CHL);                                      DLMS_dataHandler(HONGW_CHL);                                      DLMS_dataHandler(MODULE_CHL); if((((ImageparasA.TransferStatus==ImageActivationInitiated)&&(!(flag&EBIT1)))||((ImageparasB.TransferStatus==ImageActivationInitiated)&&(!(flag&EBIT2))))&&(!(flag&EBIT0)))          {                     if((memcmp((uint8_t*)&ImageparasA.ImageIdentifier[Versionoffset],(uint8_t*)&Imageparas.ImageIdentifier[Versionoffset],0x04)>0x00)&&(ImageparasA.TransferStatus==ImageActivationInitiated)&&(!(flag&EBIT1))) {                                                        ImagedataExchange((DLMS_ImageType*)&Imageparas,(DLMS_ImageType*)&ImageparasA,(DLMS_ImageType*)&ImageparasB,Code_Flash_ADDR);                    }          }          timecout++; if(timecout>SecondCounMax)          {             timecout=0;                                                 if(ImageParsDataTest((DLMS_ImageType*)&Imageparas,ImageFlash_ADDR)==TRUE)                     flag&=(uint8_t)fbit0;            else                     flag&=EBIT0;                   if(ImageParsDataTest((DLMS_ImageType*)&ImageparasA,ImagePara_ADDR)==TRUE)                    flag&=(uint8_t)fbit1;          else              flag&=EBIT1;                      if(ImageParsDataTest((DLMS_ImageType*)&ImageparasB,ImageBank_ADDR)==TRUE)              flag&=(uint8_t)fbit2;          else                    flag&=EBIT2;          CommunicationTimeout(RS485_1CHL);          CommunicationTimeout(HONGW_CHL);          CommunicationTimeout(MODULE_CHL);      if((ImageParsDataTest(&Imageparas,ImageFlash_ADDR)==TRUE)&&(Imageparas.TransferStatus==ImageActivationInitiated)) {          resetcount++;          if(resetcount>ResetCountMax)          {               ImageParas.TransferStatus=ImageActivationSuccessful;                                                 adjust_write((uint8_t*)&ImageParas,ImageFlash_ADDR,sizeof(DLMS_ImageType));                    entryrstprogram();          } } }                                    } Poweroff_IO(); while(!DiaoDianSTATE()==1) {          FreeDog(); } }          else          entryrstprogram();          } } 很明显,使用了大量的标志,并且判断复杂,在操作flash时,就会出现掉电状态错误,而导致程序滞留在引导区,很难恢复。即使用户要求版本高低,比如高版本不能回到低版本,也不建议这样判断使用。而且占用了大量的空间,在一级优化模式下居然都有12KB。而使用先擦除向量区最后写入向量区,做可靠的APP区不会超过4KB,并且成功率很高。

  • 发表了主题帖: 分享MSP430F149中断学习

    本帖最后由 火辣西米秀 于 2019-5-14 07:54 编辑 增计数模式下PWM输出原理: 用TA1实现中断,TA1溢出中断实现LED闪烁,CCIFG中断实现PWM输出控制,有些函数直接拿来用,多余的部分懒得删除了,源代码如下(基于MSP430F149): #include <msp430x14x.h> #include "Config5MHz.h" void Port_Init() { LED8SEL = 0x00; //设置IO口为普通I/O模式,此句可省 LED8DIR = 0xFF; //设置IO口方向为输出 LED8PORT = 0xFF; //P2口初始设置为FF DATASEL = 0x00; //设置IO口为普通I/O模式,此句可省 DATADIR = 0xFF; //设置IO口方向为输出 DATAPORT = 0xFF; //P4口初始设置为FF CTRSEL = 0x00; //设置IO口为普通I/O模式,此句可省 CTRDIR |= 0xFF; //设置IO口方向为输出 CTRPORT = 0xFF; //P6口初始设置为FF } void Clock_Init() { uchar i; P1DIR |= BIT4; P1SEL |= BIT4; //SMCLK output,这时候可以使用示波器观察时钟信号 P1.4 BCSCTL1&=~XT2OFF; //打开XT2振荡器 BCSCTL2|=SELM1+SELS; //MCLK为5MHZ,SMCLK为5MHZ do{ IFG1&=~OFIFG; //清楚振荡器错误标志 for(i=0;i<100;i++) _NOP(); } while((IFG1&OFIFG)!=0); //如果标志位1,则继续循环等待 IFG1&=~OFIFG; } //初始化Timer_A void TIMERA_Init() { P1DIR |= 0x04; // P1.2 output P1SEL |= 0x04; // P1.2 options select 输出PWM信号 P2DIR |= 0x01; // P2.0 output red_LED TACTL |= TASSEL1 + TACLR + MC0 + TAIE; //TAIE溢出允许,从而控制LED闪烁 TACCR0 = 50000; //设置周期为6.25ms TACCTL1 = OUTMOD_3 + CCIE; // CCR1 set/reset CCIE中断允许 TACCR1 = 25000; // CCR1 original PWM duty cycle 1/2 } void main(void) } WDT_Init(); Port_Init(); //端口初始化 Clock_Init(); //系统时钟设置 TIMERA_Init(); //设置TIMERA,P1.3输出PWM信号 __bis_SR_register(LPM0_bits + GIE); // Enter LPM0 ,Interrupt enabled __no_operation(); // For debugger } #pragma vector = TIMERA1_VECTOR __interrupt void TIMERA1_Init() { switch( TAIV ) { case 2: TACCR1 -= 100; //向CCR1减小偏移量,占空比越来越大。 break; case 4: break; case 10: P2OUT ^= 0x01; break; } //执行原理 }

  • 2019-05-13
  • 发表了主题帖: MSP430定时器控制PWM输出

    /*定时器输出单元举例: ACLK时钟频率为LFXT1=32768Hz,利用Timer_A输出周期为512/32768 =15.625ms,占空比分别为75%和25%的PWM波 */ #include "io430.h" int main( void ) {   // Stop watchdog timer to prevent time out reset   WDTCTL = WDTPW + WDTHOLD;     TACTL=TASSEL_1+TACLR;     CCR0=512-1;//PWM周期     CCTL1=OUTMOD_7;     CCR1=384;//384/512=0.75     CCTL2=OUTMOD_7;     CCR2=128;//占空比128/512=0.25     P1DIR|=BIT2;     P1SEL|=BIT2;     P2DIR|=BIT0;     P2SEL|=BIT0;     TACTL|=MC_1;//增计数模式     while(1)     {         LPM3;     }   return 0; }

最近访客

< 1/1 >

统计信息

已有4人来访过

  • 芯币:35
  • 好友:--
  • 主题:10
  • 回复:0
  • 课时:--
  • 资源:--

留言

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


现在还没有留言