STM32

STM32是STMicroelectronics(意法半导体)推出的一系列基于ARM Cortex-M内核的32位微控制器(MCU)产品。这些微控制器提供了广泛的产品系列,覆盖了多种不同的性能和功能需求,适用于各种应用领域,包括工业控制、汽车电子、消费类电子、医疗设备等。

STM32系列微控制器以其高性能、低功耗、丰富的外设接口和灵活的开发工具而闻名。它们通常具有丰富的存储器、多种通信接口(如UART、SPI、I2C、CAN等)、模拟数字转换器(ADC)、定时器、PWM输出等功能,以满足不同应用场景下的需求。

STM32微控制器通常使用标准的ARM Cortex-M内核,包括Cortex-M0、M0+、M3、M4和M7等,这些内核具有不同的性能和功耗特性,可根据具体应用的需求进行选择。此外,STM32系列还提供了多种封装和引脚配置,以满足不同尺寸和集成度的要求。

STMicroelectronics为STM32系列提供了丰富的开发工具和支持资源,包括基于ARM开发环境的集成开发环境(IDE)、调试器、评估板和参考设计等。这些工具和资源有助于开发人员快速开发和部署他们的应用,并提供了全面的技术支持和文档资料,帮助用户充分发挥STM32微控制器的性能和功能优势。

一、原理

1、红外发射协议

红外通信的协议有很多种。这个实验使用的是NEC协议。这个协议采用PWM的方法进行调制,利用脉冲宽度来表示 0 和 1 。

NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码均是 8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性。因此,每帧的数据为 32 位,包括地址码,地址反码,控制码,控制反码。反码可用于解码时进行校验比对。

NEC码的位定义:一个脉冲对应 560us 的连续载波,一个逻辑 1 传输需要 2.25ms(560us 脉冲+1680us 低电平),一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平)。而遥控接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,在接收头端收到的信号为:逻辑 1 应该是 560us 低+1680us 高,逻辑 0 应该是 560us 低+560us 高。

红外数据的波形如下图:包括一个同步头和 32 帧数据。

STM32 定时器实现红外遥控数据接收

下图可看出,同步头为 9ms 低电平加上 4.5ms 高电平,控制码为 8 个 0,控制反码为 8 个 1。

STM32 定时器实现红外遥控数据接收

2、定时器计数

定时器就是按照一个特定的频率对计数值进行加一或减一操作,当数值溢出时则产生一个标志或中断。这里是用定时器计数产生一个周期性的中断。

3、实现方法

利用定时器记录两个下降沿之间的时间,通过该时间判断是否是同步头信息、数据 1 或者数据 0。当检测到同步头,开始记录 32 个数据的时间值。

STM32 定时器实现红外遥控数据接收

二、实现

1、配置 GPIO 口下降沿触发中断

示例代码中使用 PA7 管脚,配置为上拉输入模式。

选择下降沿触发,是因为红外接收管默认情况下保持高电平,接收到数据时从高电平转变为低电平。

中断源选择为 EXTI_Line7 ,在库函数中对该中断源定义的服务函数为 EXTI9_5_IRQHandler(),也就是说外部中断 5 到 9 是 共用一个中断服务函数的。

配置代码如下:

void IR_Pin_init()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    EXTI_ClearITPendingBit(EXTI_Line7);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource7); 
    EXTI_InitStructure.EXTI_Line=EXTI_Line7;
    EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd=ENABLE;
    EXTI_Init(&EXTI_InitStructure); 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 	 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_Init(&NVIC_InitStructure);
}

2、配置定时器计数值

定时器使用的是 TIM2 通用定时器,模式为向上计数。在该模式中,计数器从 0 计数到自动加载值 (TIMx_ARR计数器的内容) ,然后重新从 0 开始计数并且产生一个计数器溢出事件。

示例函数接收两个参数,分别为预分频器的值和自动加载值。通过调整这两个参数,可以灵活地改变定时器的计数周期。例如在 TIM2 的默认时钟源 PCLK1 为96MHz时,使用语句 Tim2_UPCount_Init(SystemCoreClock/1000000-1,100-1); //0.1ms 进行初始化,可以每 0.1ms 产生一次中断。

示例代码如下:

void Tim2_UPCount_Init(u16 Prescaler,u16 Period)
{
    TIM_TimeBaseInitTypeDef TIM_StructInit;
    NVIC_InitTypeDef NVIC_StructInit;
	
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
		
    TIM_StructInit.TIM_Period=Period;
    TIM_StructInit.TIM_Prescaler=Prescaler;
    TIM_StructInit.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_StructInit.TIM_CounterMode=TIM_CounterMode_Up;
    TIM_StructInit.TIM_RepetitionCounter=0;
    TIM_TimeBaseInit(TIM2, &TIM_StructInit);
	
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM2, ENABLE);	
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	
    NVIC_StructInit.NVIC_IRQChannel=TIM2_IRQn;
    NVIC_StructInit.NVIC_IRQChannelCmd=ENABLE;
    NVIC_StructInit.NVIC_IRQChannelPreemptionPriority=0;
    NVIC_StructInit.NVIC_IRQChannelSubPriority=1;
    NVIC_Init(&NVIC_StructInit);
}

3、定时器中断函数统计时间

如上所说,定时器每 0.1ms 计数完成,产生中断,在中断函数中对标志位 ucTim2Flag 加 1,意味着时间过去了 0.1ms。

时间标志位原型为 uint16_t ucTim2Flag; 。

示例代码如下:

void TIM2_IRQHandler(void)
{
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    ucTim2Flag++;
}

4、GPIO 中断函数中接收 32 位数据

在下降沿触发的 IO 口中断函数中,需要实现统计两个下降沿之间的时间,并将其记录在数组中。

下降沿第一次触发时,清除当前定时器中的计数值,以便统计时间。之后每一次下降沿触发就记录下当前计数值,然后再对其清零。如果该时间在同步头的时间区间内,对索引进行清零,表示重新开始接收数据。

完整接收同步头和 32 个数据之后,表示接收完成。

示例代码如下:

uint8_t irdata[33];   //用于记录两个下降沿之间的时间
bool receiveComplete; //接收完成标志位
uint8_t idx;          //用于索引接收到的数值
bool startflag;       //表示开始接收
void EXTI9_5_IRQHandler(void)
{
    uint16_t ir_time;
    if(startflag)
    {
        ir_time = ucTim2Flag;	
        if(ucTim2Flag < 150 && ucTim2Flag >= 50 ) // 接收到同步头
        {
            idx=0;  // 数组下标清零
        }
		
        irdata[idx] = ucTim2Flag;  // 获取计数时间
        ucTim2Flag = 0;            // 清零计数时间,以便下次统计
        idx++;                     // 接收到一个数据,索引加1
		
        if(idx==33)       // 如果接收到33个数据,包括32位数和以一个同步头
        {
            idx=0;
            ucTim2Flag = 0;
            receiveComplete = TRUE;
        }
    }
    else   // 下降沿第一次触发
    {
        idx = 0;
        ucTim2Flag = 0;
        startflag = TRUE;
    }
	
    EXTI_ClearITPendingBit(EXTI_Line7);  // 清除中断标志
}

5、判断控制码值

由于中断函数中接收并记录下的数据是两个下降沿之间的时间,并不是红外所发送的数据。因此需要根据红外协议,对 32 个时间进行判断,从而获得红外真正发送的数据。

下面这个函数需要在红外完整接收数据后执行,可通过判断接收完成标志位 receiveComplete 来实现。

示例代码如下:

uint8_t Ir_Server()
{
    uint8_t i,j,idx=1; //idx 从1 开始表示对同步头的时间不处理
    uint8_t temp;
    for(i=0; i < 4; i++)
    {
        for(j=0; j < 8; j++)
        {
            if(irdata[idx] > =8 && irdata[idx] < 15)   //表示 0
            {
                temp = 0;
            }
            else if(irdata[idx] > =18 && irdata[idx] < 25) //表示 1
            {
                temp = 1;
            }
            remote_code[i] << = 1;
            remote_code[i] |= temp;
            idx++;
        }
    }
	
    return remote_code[2];  // 该数组中记录的是控制码,每个按键不一样
    //for(idx=0; idx < 4; idx++)
    //{
    //	printf("remote_code[%d] = %#x\n",idx,remote_code[idx]);
    //}
}

6、主函数

在 main 函数中,对 IO 口和 定时器进行初始化。

主循环中,通过判断接收完成标志位,对接收完成的按键控制码进行打印。

示例代码如下:

void main()
{
    ...
	
    IR_Pin_init();
    Tim2_UPCount_Init(SystemCoreClock/1000000-1,100-1);
 	
    while(1)
    {
        if(repeatEnable)
        {
            repeatEnable = FALSE;
            Ir_Server();
            printf("key_code = %#x\n",remote_code[2]);
        }
    }
  
}

三、演示

如下图为串口打印出接收的红外按键值信息:

STM32 定时器实现红外遥控数据接收

说明1:这只是实现红外接收的其中一种方法,网上还有一种比较常见的方法是利用下降沿触发,在中断中进行延迟,判断高电平持续时间以此来判断信号类别。个人感觉这不是一种很好的方法,因为在中断中进行延时会导致主函数得不到及时的处理。

说明2:在调试时,不要在中断处理中加入过多无关语句,例如打印语句,这会导致结果出错。

本文档基于 STM32 F1 系列 MCU,固件库版本 3.5。其他 MCU 及固件库仅需要对库函数略作修改。

作者: cyang
转自:
cyang's blog

围观 560

前言

客户反馈在批量生产阶段,发现部分产品的MCU的RTC在低温(0℃)下工作不正常,但是在常温下又是正常的,且其他正常的MCU的RTC在常温与低温下都是正常的。

问题跟进

通过与客户邮件沟通,了解到客户使用的MCU型号是STM32F030C6T6TR。在产品的主从结构中主要用作电源管理和时钟管理。通过客户的描述,似乎相同型号不同片子都存在较大的差异。

由于时间紧急,在了解到初步信息后拜访客户,针对客户认为有问题的MCU芯片做针对性试验。通过STM32CubMx生成测试工程,分别使用LSI(40K),LSE(32.768K),RTC工作时每秒通过LED1(PB5)取反一次(通过LED1灯是否闪烁来指示RTC是否工作正常),然后分别测量OSC管脚与PA8脚(输出LSI或LSE),并对比ST官方的NUCLEO-F030板,最终测试结果如下:

1、当使用LSI时,无论常温还是低温下都能正常工作。
2、当使用LSE时,常温下能正常工作,但在低温(0℃)时,RTC不再工作(LED1停止闪烁),且PA8管脚无输出,但保持为高电平,且此时OSC管脚此时是存在32.768K的波形的。
3、通过修改负载电容C1&C2的电容值从5.1pF修改到6.8pF时,原本低温下不工作的RTC又能恢复正常工作。
4、对比ST官方的NUCLEO-F030板子,在常温与低温下均能正常工作。
从测试结果来看,通过修改负载电容的方式能让原本不能正常工作的RTC恢复正常工作,这个似乎为客户的负载电容不能精准的匹配系统的原因所致。
但客户对于这个做法不接受的,理由是现在设计的负载电容5.1pF是通过测试后的值,精度可以达到6.5ppm,但如果改为6.8pF,那么精度将会变到大约30ppm,这个会影响到MCU的RTC的时间精准度,系统在长时间运行后,时间必然会偏差很大,超出设计合理范围,这个是不允许的。

问题分析

既然客户不接受修改负载电容,那么首先我们重新梳理下客户的晶振设计各种参数是否准确,客户的LSE电路设计如下所示:

低温下部分产品RTC不工作的问题探析

如上图,图中的MR10 10Mohm这个反馈电阻在实际电路中是没有加的,晶振使用的是TXC的,从晶振厂商提供的数据手册中得到相关参数如下:

低温下部分产品RTC不工作的问题探析

再者,由于客户代码中使用的LSE drive配置的是最高等级,从下图芯片对应的数据手册中可以找到对应的gm值为25uA/V,此时的驱动电流为1.6uA:

低温下部分产品RTC不工作的问题探析

前面提到过AN2867这个文档,我们打开这个文档,在3.4节,发现有这个要求:

低温下部分产品RTC不工作的问题探析

也就是要求gain margin的值要求大于5,这样晶振才能正常起振,那么gain margin又是如何计算的呢?接下来找到gainmargin 的计算公式,如下:

低温下部分产品RTC不工作的问题探析

其中gm就是图4中从数据手册中提到的跨导值,STM32F030 LSE的不同驱动等级对应着不同的gm值,由于我们的测试代码使用的是CubeMx自动生成的代码,其默认使用的是最高等级,且客户使用的也是最高等级,因此,这个得出的gm值为25 uA/V, gm有了,那么上面公式中的gmcrit又该如何计算,我们接下来找到它的计算公式,如:

低温下部分产品RTC不工作的问题探析

通过晶振对应参数,我们可以得出:
ESR =70KΩ,C0 =1.0pF, CL =7.0pF, 而F就是LSE的频率,为32.768KHz.
于是:
g_mcrit =4 * 7E4 *POWER(2*PI()*32768,2) * POWER ((1.0E-12 + 7.0E-12),2) =7.6E-07
最终得到:
gain_magin =gm/g_mcrit=2.5E-05/7.6E-07 =32.89
这个值是远大于5,因此,理论上不会存在晶振不起振是的问题,实际上当在低温下,之前在测试中也有发现晶振也是有起振,有波形输出的,只不过PA8脚没有波形输出,那个又是什么问题呢?
最终定位到LSE的驱动等级过高,在AN2867这个文档中,有这样的描述:

低温下部分产品RTC不工作的问题探析

也就是说,在STM32F0和STM32F3中,当使用最高驱动模式(gm_crit_max=5uA/V, 见Figure9gm_crit_max)时,对应地应该只使用在CL=12.5pF的晶振上,以此避免振荡回路饱和,从而导致启动失败。若此时使用了一个较小的CL(如CL=6pF),那么会导致振荡频率不稳定和工作周期可能被扭曲。

AN2867随后给出了一张表,列出了驱动等级与gm_min、gm_crit_max的关系,如下:

低温下部分产品RTC不工作的问题探析

如上图,对于STM32F0,当使用最高驱动模式High时,此时的gm_min=25 uA/V,这个与数据手册中是一致的,另外gm_crit_max=5uA/V,正是上面所描述的。
也就是说,在使用最高驱动模式下,此时与之对应的CL应该使用12.5pF,而客户所使用的CL是7pF,这个与手册AN2867的建议内容是不相符的。从图4可以看出,在最高驱动等级模式下,此时驱动电流最大(1.6uA),但这里使用了一个比较小的负载电容(CL=7pF),按AN2867所述,此时有可能导致振荡回路饱和,振荡不稳定,工作周期扭曲。
此时,应该对应地下调这个LSE驱动等级,减小驱动电流,这里有4档(见Figure9):Low,Medium Low,Medium High,High. 目前使用的是High,正是它出了问题,为保守起见,使用Medium High相对合适。

低温下部分产品RTC不工作的问题探析

如上图,将LSEDRV[1:0]这两个为修改为10即可,将原先低温下RTC有问题的MCU芯片修改后再次放到低温下进行验证,测试结果为正常。由于此问题是部分芯片有可能会出现的问题,客户需要对修改后的芯片进行持续跟踪,至今没有再反馈出现过此问题,由此,此问题基本解决。

总结

AN2867这个文档总结了关于STM32晶振匹配方面的信息。里边有提到,负载电容CL值越大,所需的驱动电流也就越大,但牵引度越小。这也就解释了表1中通过增大C1&C2的电容值,原本出现问题的RTC能恢复正常的现象,这是由于C1&C2的电容值变大将导致负载电容CL变大,进而对应所需的驱动电流也就跟着增加,这反而减少了在高驱动模式情况下振荡回路出现饱和的机会。

来源: STM32单片机

围观 742

众所周知STM32有5个时钟源HSI、HSE、LSI、LSE、PLL,其实他只有四个,因为从上图中可以看到PLL都是由HSI或HSE提供的。

图文教你理解单片机STM32时钟

其中,高速时钟(HSE和HSI)提供给芯片主体的主时钟.低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用,图中可以看出高速时钟也可以提供给RTC。内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。而外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号.

高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,我们一般采用8MHz的晶振。

高速内部时钟(HSI): 由内部RC振荡器产生,频率为8MHz,但不稳定。

低速外部时钟(LSE):以外部晶振作时钟源,主要提供给实时时钟模块,所以一般采用32.768KHz。

低速内部时钟(LSI):由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。

OSC_OUT和OSC_IN开始,这两个引脚分别接到外部晶振8MHz,第一个分频器PLLXTPRE,遇到开关PLLSRC(PLL entry clock source),我们可以选择其输出,输出为外部高速时钟(HSE)或是内部高速时钟(HSI)。这里选择输出为HSE,接着遇到锁相环PLL,具有倍频作用,在这里我们可以输入倍频因子PLLMUL,要是想超频,就得在这个寄存器上做手脚啦。经过PLL的时钟称为PLLCLK。

倍频因子我们设定为9倍频,也就是说,经过PLL之后,我们的时钟从原来8MHz的 HSE变为72MHz的PLLCLK。紧接着又遇到了一个开关SW,经过这个开关之后就是STM32的系统时钟(SYSCLK)了。通过这个开关,可以切换SYSCLK的时钟源,可以选择为HSI、PLLCLK、HSE。我们选择为PLLCLK时钟,所以SYSCLK就为72MHz了。

PLLCLK在输入到SW前,还流向了USB预分频器,这个分频器输出为USB外设的时钟(USBCLK)。回到SYSCLK,SYSCLK经过AHB预分频器,分频后再输入到其它外设。如输出到称为HCLK、FCLK的时钟,还直接输出到SDIO外设的
SDIOCLK时钟、存储器控制器FSMC的FSMCCLK时钟,和作为APB1、APB2的预分频器的输入端。GPIO外设是挂载在APB2总线上的, APB2的时钟是APB2预分频器的输出,而APB2预分频器的时钟来源是AHB预分频器。因此,把APB2预分频器设置为不分频,那么我们就可以得到GPIO外设的时钟也等于HCLK,为72MHz了。

SYSCLK: 系统时钟,STM32大部分器件的时钟来源。主要由AHB预分频器分配到各个部件。

HCLK: 由AHB预分频器直接输出得到,它是高速总线AHB的时钟信号,提供给存储器,DMA及cortex内核,是cortex内核运行的时钟,cpu主频就是这个信号,它的大小与STM32运算速度,数据存取速度密切相关。

FCLK: 同样由AHB预分频器输出得到,是内核的“自由运行时钟”。“自由”表现在它不来自时钟 HCLK,因此在HCLK时钟停止时 FCLK 也继续运行。它的存在,可以保证在处理器休眠时,也能够采样和到中断和跟踪休眠事件 ,它与HCLK互相同步。

PCLK1: 外设时钟,由APB1预分频器输出得到,最大频率为36MHz,提供给挂载在APB1总线上的外设,APB1总线上的外设如下:

RCC_APB1Periph_TIM2 TIM2时钟

RCC_APB1Periph_TIM3 TIM3时钟

RCC_APB1Periph_TIM4 TIM4时钟

RCC_APB1Periph_WWDG WWDG时钟

RCC_APB1Periph_SPI2 SPI2时钟

RCC_APB1Periph_USART2 USART2时钟

RCC_APB1Periph_USART3 USART3时钟

RCC_APB1Periph_I2C1 I2C1时钟

RCC_APB1Periph_I2C2 I2C2时钟

RCC_APB1Periph_USB USB时钟

RCC_APB1Periph_CAN CAN时钟

RCC_APB1Periph_BKP BKP时钟

RCC_APB1Periph_PWR PWR时钟

RCC_APB1Periph_ALL 全部APB1外设时钟

PCLK2:外设时钟,由APB2预分频器输出得到,最大频率可为72MHz,提供给挂载在APB2总线上的外设,APB2总线上的外设如下:

RCC_APB1Periph_TIM2 TIM2时钟

RCC_APB2Periph_AFIO 功能复用IO时钟

RCC_APB2Periph_GPIOA GPIOA时钟

RCC_APB2Periph_GPIOB GPIOB时钟

RCC_APB2Periph_GPIOC GPIOC时钟

RCC_APB2Periph_GPIOD GPIOD时钟

RCC_APB2Periph_GPIOE GPIOE时钟

RCC_APB2Periph_ADC1 ADC1时钟

RCC_APB2Periph_ADC2 ADC2时钟

RCC_APB2Periph_TIM1 TIM1时钟

RCC_APB2Periph_SPI1 SPI1时钟

RCC_APB2Periph_USART1 USART1时钟

RCC_APB2Periph_ALL 全部APB2外设时钟

来源: 嵌入式资讯精选

围观 664

前言

STM32 PCROP专有代码读出保护,将某个区域设置为仅允许执行,可防止代码被非法读出与修改。ST网站提供了免费的PCROP参考代码,但是例程中仅仅提供了用代码设置PCROP。为方便利用PCROP进行知识产权保护的开发和部署,这篇文章提供了方法,可在RDP级别设置为1或者0时,使用代码清除PCROP。

ST网站上的PCROP参考代码

学习使用PCROP,可从ST网站下载文档以及参考代码。文档里有一步一步的详细说明。参考代码则实现了,如何设置编译开发环境去掉文字库(Literal pool),以避免受保护区域需要被读访问;参考代码也实现了如何利用代码使能PCROP保护以及如何导出接口符号供二次开发使用。

你可以编译运行PCROP参考代码。一旦下载到开发板并运行后,扇区2会自动被设置成PCROP保护。你将无法再次下载代码到该扇区,也无法读出该扇区的内容。若想通过STLink工具解除PCROP保护,则会导致整个Flash被擦除。

使用代码清除PCROP

在熟悉ST网站上的PCROP参考代码基础之上,我们将讨论如何使用代码清除PCROP。

1. 原理

根据STM32用户手册,要想清除PCROP保护,读保护RDP级别必须从1设置成0。也就是说,即使当前RDP级别为0,我们也要使用代码将其设置成1。然后,同时关掉PCROP和将RDP设置成0。这也说明,尽管是清除PCROP保护,我们的代码必须加入RDP的设置函数, 而不能仅仅修改参考代码中的PCROP_Enable的状态字段使其变成PCROP_Disable。

解读STM32单片机:代码实现 PCROP清除

2. 材料准备

开发板: STM32F429I_Discovery

开发工具:
STM32Cube_FW_F4_V1.15.0
STM32CubeExpansion_AN4701_F4_V1.0.0(从ST网站下载的参考代码)
STM32 STLink
IAR/Keil

注:也可以选择其他支持PCROP的STM32系列,并选择相应的STM32开发板与STM32固件库。

3. 代码

• 设置RDP到级别1

该函数在RDP级别为0时,若需要清除PCROP, 必须被使用。

解读STM32单片机:代码实现 PCROP清除

• 设置RDP到级别0

在清除PCROP保护的代码里不会直接调用这个函数。参考手册提到,PCROP的清除必须与RDP从1到0同时发生,而下列RDP_Disable函数则是完整独立的,无法与PCROP的Option bytes同时操作。不过,这个代码的中间部分,也就是实际功能部分,将在清除PCROP时被重用。

解读STM32单片机:代码实现 PCROP清除

解读STM32单片机:代码实现 PCROP清除

• 清除PCROP

下述代码清除PCROP,它基于参考代码中的PCROP_Enable函数改写而成。首先,它通过RDP_Enable将RDP设置成1。注意实验中不要将RDP设置成为2,否则所有的Option bytes将不再被允许修改。然后将RDP和PCROP都设置完毕,调用一次HAL_FLASH_OB_Launch达到同时将RDP设置成1并清除PCROP保护。

解读STM32单片机:代码实现 PCROP清除

解读STM32单片机:代码实现 PCROP清除

4.运行

在主函数中,调用PCROP_Disable可解除PCROP保护。RDP_Enable后需要关闭电源,重新启动,然后系统正常运行解除PCROP保护。解除保护后,可通过STLink确认PCROP Option bytes已恢复,同时也可以看到整个Flash内容已被擦除。

结论

本文讨论了完全使用代码控制PCROP的设置与清除。它可以使用在PCROP代码保护的开发与部署阶段。

来源: eeworld.com

围观 644

前言

STM32全系列产品都具有CRC外设,对CRC的计算提供硬件支持,为应用程序节省了代码空间。CRC校验值可以用于数据传输中的数据正确性的验证,也可用于数据存储时的完整性检查。在IEC60335中,也接受通过CRC校验对FLASH的完整性进行检查。在对FLASH完整性检查的应用中,需要事先计算出整个FLASH的CRC校验值(不包括最后保存CRC值的字节),放在FLASH的末尾。在程序启动或者运行的过程中重新用同样的方法计算整个FLASH的CRC校验值,然后与保存在FLASH末尾的CRC值进行比较。

EWARM从v5.5版本之后开始支持STM32芯片的CRC计算。前面所说的计算整个FLASH的CRC校验值并保存在FLASH末尾的过程,可以在IAR中完成。通过配置EWARM的CRC计算参数,自动对整个FLASH空间进行CRC计算,并将计算结果放到FLASH的末尾。本文中将介绍的就是如何配置IAR的CRC参数,使之与STM32的CRC硬件模块保持一致。本文中的例子都基于STM32F072进行。

STM32的CRC外设

CRC校验值的计算采用多项式除法,可以通过除数和被除数进行异或运算实现。这种方法非常适合通过硬件电路来实现。

使用STM32CRC外设时,你要考虑的内容包括:采用哪个CRC生成多项式,输入数据(要进行校验的数据)和初始值。

1.生成多项式

默认使用CRC32多项式:0x4C11DB7

如何在IAR中配置CRC参数

部分芯片支持可编程的多项式,比如STM32F3,STM32F0,STM32L0

2.初始值

STM32的CRC初始值默认为0xFFFFFFFF,STM32F3,STM32F0,STM32L0系列可以修改初始值

3.输入/输出数据的反转

STM32F3,STM32F0,STM32L0系列还提供了对输入/输出数据进行反转的功能。
默认不对输入数据和输出数据进行位反转

• 对输入数据的位反转操作可以设置为按字节/半字 /字为单元进行操作。例如输入数据为0x1A2B3C4D,
- 每个字节内逐位反转,结果是0x58D43CB2
- 每半字内逐位反转,结果是0xD458B23C
- 每个字长内逐位反转,结果是0xB23CD458
• 对输出数据的位反转。
- 例如输出数据为0x11223344,反转后为0x22CC4488

IAR的CRC配置

1.修改Link文件

指定checksum在FLASH中的存储位置,在Link文件中增加下面语句。

如何在IAR中配置CRC参数

该语句指定将CRC的值放在FLASH的末尾位置。是整个FLASH空间的末尾,不是应用程序的代码末尾。这样,CRC值的位置就是固定的。不会随代码大小而变化。

2.配置Checksum页面的参数

IAR Checksum页说明(v6.4及以上)

如何在IAR中配置CRC参数

IAR的checksum页面分为两个部分。
第一部分,也就是红线圈出的部分。定义了FLASH中需要计算CRC的范围和空闲字节填充值。
剩下的部分,就是对checksum计算参数的设定部分。

Checksum size :选择checksum的大小(字节数)
Alignment:指定checksum的对齐方式。不填的话默认2字节对齐。
Algorithm:选择checksum的算法
Complement:是否需要进行补码计算。选择“Asis”就是不进行补码计算。
Bit order:位输出的顺序。MSB first,每个字节的高位在前。LSB first,每个字节的低位在前。
Reverse byte order within word: 对于输入数据,在一个字内反转各个字节的顺序。
Initial value: checksum计算的初始化值
Checksum unit size :选择进行迭代的单元大小,按8-bit,16-bit还是32-bit进行迭代。

3.STM32 CRC外设使用默认配置时IAR的配置

STM32CRC外设的配置:

POLY= 0x4C11DB7(CRC32)
Initial_Crc = 0Xffffffff
输入/输出数据不反转
输入数据:0x08000000~0x0801FFFB。(最后4个字节用来放计算出的CRC值)

如何在IAR中配置CRC参数

如何在IAR中配置CRC参数

具体使用中我们可以根据实际应用需求参照上面介绍做相应配置即可。

围观 576

随着集成电路的发展和数字信号处理技术的采用,数字示波器已成为集显示、测量、运算、分析、记录等各种功能于一体的智能化测量仪器。数字示波器在性能上也逐渐超越模拟示波器,并有取而代之的趋势。与模拟示波器相比,数字示波器不仅具有可存储波形、体积小、功耗低,使用方便等优点,而且还具有强大的信号实时处理分析功能。因此,数字示波器的使用越来越广泛。目前我国国内自主研发的高性能数字示波器还是比较少,广泛使用的仍是国外产品。因此,有必要对高性能数字示波器进行广泛和深入研究。

本文通过采用高速高性能器件,设计了一实时采样率为60 msa/s的宽带数字示波器。

1 数字示波器的性能参数设计

数字存储示波器的指标很多,包括采样率、带宽、灵敏度、通道数、存储容量、扫描时间和最大输入电压等。其中关键的技术指标主要有采样率、垂直灵敏度(分辨率)、水平扫描速度(分辨率)。这几项指标直接与所选a/d、fifo和高速运放器件的性能,以及电路设计有关。下面根据所选器件的性能参数,合理地分析和确定示波器的采样率和分辨率。

1.1 采样率与水平扫描分辨率

采样率主要取决于a/d转换器的转换速率,常用每秒取样点数sa/s(sample/second)来表示。本系统设计最高实时采样率为60msa/ s,若进一步提高采样率可采用文献提出的等效采样技术,不过等效采样技术的软硬件和价格成本很高。为了使示波器具有较高的信号波形分析细节,采用数字内插技术来恢复和重建信号波形。文献中详细论述了线性内插和正弦内插算法在示波器设计中的应用问题。因此,对这两种内插算法不再详细论述,在本文设计中直接引用文献中的研究成果。根据文献研究结果,取信号每周期采样点数为20,插值倍数为4。水平显示像素点数为400个,共10格。水平扫速与采样时钟频率的关系表如下。

基于stm32的数字示波器设计方案

1.2 垂直灵敏度

垂直分辨率的高低直接影响数字示波器对波形细节的显示,垂直分辨率越高,则示波器上的信号波形细节越小,它取决于a/d转换精度和tft的显示分辨率。本文设计中取最大采样输入电压为2 vpp,垂直刻度为8格,共256个像素点,因此垂直精度为0.25 v/格。共设计9个灵敏度档位,每档灵敏度与程控放大倍数的关系如表2所示。

基于stm32的数字示波器设计方案

2 数字示波器的硬件设计

2.1 系统硬件总体框图

系统硬件总体框图如图1所示,主要由stm32控制单元,信号输入阻抗匹配单元,信号调理单元,a/d采样与fifo存储单元,时钟单元,tft显示单元等组成。输入信号经阻抗匹配后,送入信号调理单元,将信号的幅度放大或衰减到适合a/d采样的范围内,a/d采样单元对幅度为2vpp的信号进行a/d采样,并将采样结果存入fifo单元中。cpu从fifo中读存数据并进行内插运算,然后根据用户通过键盘输入的指令将信号波形显示在tft液晶屏上。另外,cpu还可以将数据通过rs232接口上传给上位机,或进行打印等处理。

基于stm32的数字示波器设计方案

2.2 输入阻抗匹配电路

对于低速数据采集,由于信号反射对信号的传输过程影响微乎其微,所以低速数据采集系统良好的高阻抗性能,对提高系统的测量精确度有很大的意义。本设计中采用电压跟随器实现阻抗变换,数据采集阻抗变换电路的设计方案如图2所示,其输入阻抗为10mω。

基于stm32的数字示波器设计方案

2.3 信号调理电路

信号调理电路主要采用具有可变增益的数字程控放大器ad8260。ad8260是ad公司生产的一款大电流驱动器及低噪声数字可编程可变增益放大器。该器件增益调节范围为-6 db~+24 db,可调增益的-3 db带宽为230mhz,可采取单电源或双电源供电。主要用于数字控制自动增益系统、收发信号处理等领域。本设计主要使用其数字控制自动增益功能。ad8260内部的数字程控增益功能框图如图3所示。经阻抗匹配后的信号可直接输入ad8260的17、18脚,经ad8260内部前端放大器6 db的固定增益放大,-30 db程控衰减以及末级放大器18 db固定增益放大后,由7和8脚输出。第11、12、13、14脚为四位数字控制信号(d0、d1、d2、d3),与stm32的i/o口直接连接,实现增益控制。表3给出了ad8260增益调节真值表。

基于stm32的数字示波器设计方案

基于stm32的数字示波器设计方案

2.4 a/d和fifo电路

在数据采集电路设计中,选用bb公司的8位高速ad转换器ads830e,最高采样频率为60 msa/s,最低采样频率为10 ksa/s。8位转换精度的显示分辨率为256格,能够满足所选用分辨率为640*480的tft显示模块。fifo存储器采用idt7204高速缓存,其缓存深度达1 024 k。fifo存储器是一种双口的sram,没有地址线,随着写入或读取信号对数据地址指针进行递加或递减,来实现寻址。

2.5 时钟电路

时钟产生电路为ad转换器提供一系列的采样时钟信号,共有8种频率,分别对应着不同的水平扫速。时钟产生电路主要由高稳定度的温补晶振,分频器74ls390,多路选择器74f151以及分频器74f74触发器构成。基准时钟信号由一块60 mhz的温度补偿型有源晶体模块提供,输出的60 mhz信号经过分频器的多次分频得到8种不同的频率,然后送入多路选择器74f151。stm32通过对74f151的三根选通信号线进行控制来选择所需的采样频率。另外,中央控制器采用stm32处理器,主频设为80 mhz。显示器采用分辨率为640*480的tft显示模块,与stm32之间采用spi接口。与其它上位机通信采用rs232口。

3 系统软件设计

系统软件设计采用模块化设计方法,整个程序主要由初始化程序、人机交互菜单程序、键盘扫描程序、触发程序、显示程序和数据采集及频率控制程序组成。系统软件的流程图如图4所示。

基于stm32的数字示波器设计方案

4 实验测试

在实验室对研制的样品机进行了测试实验,图5和图6分别显示了频率为16.2 khz和1 khz的方波信号。由测试数据分析可得:垂直灵敏度满足要求,电压测量误差≤5%,输入端输入阻抗大于2 mω,实验结果达到了设计要求。

基于stm32的数字示波器设计方案

基于stm32的数字示波器设计方案

5 小结

为实现一个高采样率,宽频带的便携式数字存储示波器,设计了以stm32为控制核心的数字示波器。硬件平台主要采用了ad8260数字程控增益放大器作为前端信号调理电路,ads830高速宽带模数转换器和idt7204高速缓存作为数字采集电路,以及信号波形采用了tft彩屏显示。另外,通过采用数字内插的数字信号处理算法来重建和还原信号波形,进而改善了信号波形显示细节。最后对研制样品进行了实验室测试,实验结果表明硬件设计思路与软件及算法的处理是正确的,性能参数达到设计要求,可以应用在工程实践中。

数字示波器在信号显示,处理以及带宽等方面比传统模拟示波器更有优势,因此数字示波器是今后示波器发展的重要方向。本文采用stm32高性能arm处理器作为核心控制芯片,能够满足tft彩色波形显示,数字插值算法处理等。通过采用高速ad和fifo器件,实现了高采样率,宽频带的技术要求。

来源: 中电网

围观 873

S-bus为futaba使用的串行通信协议。实际上为串口通信。但是有几点需要注意:

1. 在大端小端上,网上资料都说的不是很清楚;
2. 跟TTL串口信号相比,S-bus的逻辑电平是反的,需用如下电路对电平反相,再借到串口接收的Rx管脚就可以了;
STM32 解析futaba S-bus协议
一、协议说明:

串口配置为波特率100kbps,8位数据,偶校验(even),2位停止位,无流控。
链接
https://mbed.org/users/Digixx/notebook/futaba-s-bus-controlled-by-mbed/说明了S-bus帧格式。每帧25个字节,按照如下顺序排列:
[startbyte] [data1] [data2] .... [data22] [flags][endbyte]

起始字节startbyte = 11110000b (0xF0),但实际上用STM32(据说ARM核)收到的是0x0F。中间22个字节就是16个通道的数据了,为什么是16个通道?因为22x8=11x16,每个通道用11bit表示,范围是0-2047。不信看波形图:

STM32 解析futaba S-bus协议

什么,还看不清?
STM32 解析futaba S-bus协议

两帧之间的时间间隔4ms(高速模式),约7ms一帧。
STM32 解析futaba S-bus协议

STM32 解析futaba S-bus协议

STM32 解析futaba S-bus协议

基本而言,data1为ch1的低8位,data2的低3位为ch1的高三位,data2的高5位是ch2的低5位,data3的低6位是ch2的高6位,以此类推,如下图所示:
STM32 解析futaba S-bus协议

flags的结构如下所示:
flags:
bit7 = ch17 = digital channel (0x80)
bit6 = ch18 = digital channel (0x40)
bit5 = Frame lost, equivalent red LED on receiver (0x20)
bit4 = failsafe activated (0x10)
bit3 = n/a
bit2 = n/a
bit1 = n/a
bit0 = n/a



endbyte为0x00。

一、程序实现:
在STM32中的具体实现,除了如上述内容配置串口参数,还需要写好中断函数,写好解析函数。思路很简单,利用间隔时间来区分两帧,收到一帧数据后,做如下检查:
1. 字节数够不够?
2. 第一个字节是不是0x0f?
3. 最后一个字节是不是0x00?
4. 检查flag中的标志位
举个中断函数栗子:
void UART4_IRQHandler(void)
{
static uint8_t byteCNT = 0;

static uint32_t lastTime = 0;
uint32_t curTime;
uint32_t interval = 0;

HAL_NVIC_ClearPendingIRQ(UART4_IRQn);

//如果时间间隔大于3毫秒,则认为是新的一帧
if(lastTime == 0)
{
curTime = HAL_GetTick();
lastTime = curTime;
}
else
{
curTime = HAL_GetTick();
interval = curTime - lastTime;
lastTime = curTime;

if(interval >= 3)
{
if(byteCNT == 25 && uart4_cache1[0] == 0x0f && uart4_cache1[24] == 0x00)
{
rc_captured = 0;
memcpy(uart4_cache2, uart4_cache1, byteCNT);
rc_captured = 1;
}
byteCNT = 0;
}
}

if(RESET != __HAL_UART_GET_FLAG(&huart4, UART_FLAG_ORE))
{
__HAL_UART_CLEAR_FLAG(&huart4, UART_FLAG_ORE);
uart4_cache1[byteCNT++] = huart4.Instance->DR;
}

if(RESET != __HAL_UART_GET_FLAG(&huart4, UART_FLAG_RXNE))
{
uart4_cache1[byteCNT++] = huart4.Instance->DR;
}

}

来源: 与非网

围观 546

前言

某客户在使用我们的STM32L073芯片做项目的开发,据他们的工程师反映在测量低功耗模式下的唤醒时间,他们测试得到的数据与数据手册中列出的结果不符合,而且差别很大,并且测试了很多片都是这个问题。想咨询我们什么样的测试方法能够得到一个符合手册规范的数值。

一、测试

在这里正好选取了手边有的STM32L053C8-Discovery探索板。
软件里选取“…STM32Cube_FW_L0_V1.9.0\Projects\STM32L053C8-Discovery\Examples\PWR” 目录下的PWR_STANDBY 和PWR_STOP这项目工程,通过这两个低功耗模式做一个说明测量唤醒时间的方法。

1.1 PWR_STANDBY模式

查看相应的参考手册RM,了解standby模式下的特点,主要涉及到参考手册中的如下两个表格:

STM32L低功耗模式下唤醒时间的测量

STM32L低功耗模式下唤醒时间的测量

从这两个表格中,我们可以看到其进入低功耗模式的条件,退出模式的条件,退出后执行的情况。对应表格可以看出,退出STANDBY模式后执行的是RESET复位,唤醒的方法我们选择WKUP唤醒引脚的上升沿;进入低功耗的方法有WFI(wait for interrupt)和WFE(wait for event)。

进入低功耗确认

直接打开运行“…STM32Cube_FW_L0_V1.9.0\Projects\STM32L053C8-Discovery\Examples\PWR” 目录下的PWR_STANDBY项目工程,并且阅读工程目录下的Readme.txt,了解该项目代码是如何进入低功耗模式,以及进入低功耗的寄存器及时钟方面的配置,这里不再赘述,重点是唤醒时间的测量。

STM32L低功耗模式下唤醒时间的测量

将JP4跳线帽拔除,将万用表调至电流档位,串入万用表,全速运行项目代码,观测电流功耗,按下B1的按钮,看看电流的差别,判断程序运行是否正常即可(进入低功耗模式前后电流)。

唤醒波形的设置

此时程序已经能够正确的进入并能够退出低功耗模式,但由于Discovery探索板上是采用机械按钮B1的唤醒(通过查看该探索板的原理图,可以发现B1连接到的是MCU的PA0引脚,WKUP引脚),基于机械按钮在按下或释放按钮的时候,电平变高或变低的时候,或存在坡度(按键的抖动和按键电路中电容的影响),这样不利于观察唤醒时间的读取,所以可以的操作是:1/去除B1按键相关的电路,比如电容等,使得与其相连的PA0引脚上面没有电路,这时候可以从外部引入发波的波形进入PA0。2/软件代码里配置其它的唤醒引脚输出脉冲用于唤醒;唤醒引脚上波形的上升沿用于唤醒低功耗模式;

唤醒后的第一条语句的执行

从前面的分析可以知道,STANDBY模式唤醒后是执行的复位操作,即对应于IAR项目程序中的Reset Handler。在Reset Handler中添加引脚状态的切换(由低变高,或由高变低),引脚边沿的变化即可理解为唤醒后开始执行第一条语句的时间;这里添加Reset Handler中的函数最好是汇编代码,如果是C语言代码的话,由于编译工具的优化,可能C语言的一句代码,成为汇编语言的话会变成好几条,这就会影响唤醒时间的测量。

STM32L低功耗模式下唤醒时间的测量

如果仅仅为了了解测试方法简化使用,可以看到Reset_Hanlder执行的第一个函数是SystemInit,所以简化一点,可以在SystemInit函数的开始添加如下的代码用于判断唤醒后的第一条语句:
STM32L低功耗模式下唤醒时间的测量

唤醒时间的计算

唤醒时间的测量,可以读取时间段= [唤醒引脚上波形的上升沿用于唤醒低功耗模式,引脚边沿的变化即可理解为唤醒后开始执行第一条语句的时间],也就是两个跳动边沿的时间间隔。

实验结果:

黄色的波形代码的是唤醒引脚PA0上的上升沿,是用于将MCU从Standby模式下唤醒;
蓝色的波形代码的是,MCU从STANDBY模式唤醒后,执行的第一个语句翻转IO口,
可以看出,STANDBY模式唤醒的时间测试结果大致为:70us,快速唤醒,符合数据手册上的描述:

STM32L低功耗模式下唤醒时间的测量

1.2 PWR_STOP模式

同样的操作步骤和上述STANDBY模式类似,只是通过参考手册表格我们可以知道,唤醒STOP模式主要用到的是外部中断事件,WKUP引脚不能唤醒了,唤醒后不是执行RESET服务,而是继续执行进入STOP模式后的下一条指令。

所以基于上述的分析:通过打开示例程序“…STM32Cube_FW_L0_V1.9.0\Projects\STM32L053C8-Discovery\Examples\PWR” 目录下的PWR_STOP项目,主要进行修改如下四个方面:

1/ 系统时钟初始化
修改测试的条件和数据手册中的条件一致,修改SystemClock_Config()函数,这里选取的是系统时钟SYSCLK = 32MHz,HCLK = 16MHZ = HSI;

STM32L低功耗模式下唤醒时间的测量

2/ 测试端口的配置PB12
添加for testing部分的代码,配置PB12为EVENTOUT模式,事件输出模式,结合__SEV()指令,用于在PB12的引脚上输出一个脉冲,单周期指令;
STM32L低功耗模式下唤醒时间的测量

3/STOP模式的进入
STM32L低功耗模式下唤醒时间的测量

修改成如下的配置:

注意这里用到的PWR_STOPENTRY_WFE,而不是PWR_STOPENTRY_WFI,这样可以避免需要外部的中断事件唤醒处理等的时间,所以这里也可以发现在中断函数中,由于配置为外部中断,下面这个函数在原项目工程中不再起作用了。

STM32L低功耗模式下唤醒时间的测量

STM32L低功耗模式下唤醒时间的测量

也就是说PA0引脚被配置为GPIO_MODE_EVT_FALLING,外部事件模式,而不是外部中断模式。PA0的下降沿用于唤醒STOP模式。
4/唤醒后的第一条指令
修改HAL_PWR_EnterSTOPMode()函数,

STM32L低功耗模式下唤醒时间的测量

只添加for testing部分的这一条__SEV()指令;用于唤醒后第一条执行指令也就是在PB12引脚上输出一个脉冲。
直接运行程序,
发现实验的结果为:
黄色波形为PA0的唤醒波形
蓝色波形为__SEV指令作用于PB12引脚的脉冲。
STM32L低功耗模式下唤醒时间的测量

所以可以看出,STOP模式下唤醒的时间大约为5.2us,
数据手册中的数据为typ 4.9us,max 7 us,符合手册的说明要求。

STM32L低功耗模式下唤醒时间的测量

二、总结

通过上述的两个示例可以看出,通常结合外部模式(外部中断模式External Interrupt Mode或外部事件模式External Event Mode)和__SEV()指令翻转GPIO口来测量低功耗模式下的唤醒时间测量。
由于外部事件模式不需要额外处理中断时间的特点,所以利用它,我们能够得到更精确的唤醒时间的测量,更适合用于唤醒后执行下一条命令的低功耗模式。

来源: 21ic

围观 657

设计一个可以实现短信收发与数据无线传输的模块的要求,本文采用了ARM Cortex—M3内核的主流产品STM32作为主控芯片,采用SIMCom公司的SIM900A作为通信芯片。在查阅大量相关文献以及相关芯片的数据手册之后,本文设计了一个远程无线通信模块。该模块在实验室试运行一周后,没有出现掉线的情况,数据收发的速度也很快。该模块具有性能稳定,外形小巧,性价比高等优点。厂方投入使用之后,反应良好。

随着网络和现代通信技术的不断发展,远程无线通信技术经过多年的研究与实际应用,现如今在工业控制领域有了非常重要的地位,并且发挥着越来越大的作用。文中根据厂家的要求,设计的无线通信模块,主要实现了短信与数据收发功能,并且做到了模块的稳定,掉线之后能够自动重连。

1、无线通信模块整体设计方案

模块主要有电源部分、主控部分、通信部分、数据传输部分4个部分组成。通信模块采用SIM900A进行无线通信。主控模块采用STM32作为主控芯片,来控制短信的收发与数据传输的顺利进行。远程终端可以是手机或者上位机的数据中心软件,经过处理之后,储存下来,方便日后的查询。本模块具有低功耗,方便灵活,操作简单并且稳定,掉线之后可以自动连接,运行过程十分稳定,并且成本较低。

2、无线通信模块硬件设计

2.1 功能需求

本文设计的无线通信模块,要求单12V电源输入,模块上电运行后有相应的指示灯来指示模块的运行状态。另外要求模块能够实现短信以及数据透传两大主要功能,在手机发送短信给模块之后,模块能够动作,并给出回应。最后要求在建立TCP链接时,仍然能够收发短信。

图1 无线通信模块结构图

2.2 电源部分

在主电路中,主控芯片STM32的工作电压为2.0~3.6 V,通信芯片SIM900A的工作电压为3.1~4.6 V,为使模块各个部分正常工作,必须对两者进行分别供电,电源供电电路如下:

图中V12外接12 V电源,经过电容滤波后输入到LM2576,实现12 V到4 V的转换,R1、R2在线路中起到分压作用,D2灯亮起时,表示模块已经正常供电。LM2576是美国国家半导体公司生产的3 A电流输出降压开关型集成稳压电路,具有完善的保护电路,比较稳定。

图2 电源模块

2.3 主控芯片

主控模块采用STM32单片机作为微控制器,该芯片能工作于-40~105℃的温度范围,MAX3232芯片用于串行口的电平变换,实现控制器与通信接口之间的通信。串口1与电源电平转换芯片Max3223相连,USART1_TX(输出,所以在配置GPIO时,定义该口的模式为推拉输出,USART1_ RX为输入,定义为悬浮输入模式。串口2与SN65LBC184D通信,实现数据的收发,USART2_RTS、USART2_RX为输入端口,模式定义为悬浮输入模式,USART2_CTS、USART2_TX为输出端口,模式定义为推拉输出。串口3用来控制SIM900A芯片,USART3_RTS、USART3_RX为输入端口,模式定义为悬浮输入,USART3_CTS、USART3_TX为输出端口,模式定义为推拉输出。

图3 主控芯片STM32

2.4 通信芯片

通信芯片采用SIMCom公司的新型紧凑型产品SIM900A,它属于双频GSM/GPRS模块,完全采用SMT封装形式,性能稳定,外观精巧,性价比高,并且能够满足用户的多种需求。在实现断线自动重连功能时,涉及到DCD、RI两个引脚的使用。DCD引脚用来实现模数转换,当模块掉线时,会给DCD引脚一个高电平,当这个电平被DCD引脚检测到之后,模块就是采取相应的动作,来重新连接上线。RI引脚在模块上线之后,就一直保持高电平,在有电话和短信进来的时候,RI管脚就会有一个低电平出现,当RI引脚检测到这个低电平的时候,模块就会采取相应动作,进入到短信或者电话模式。

图4 SIM900A通信芯片

3、无线通信模块软件设计

3.1 STM32的底层配置

为了实现STM32单片机与SIM900A模块之间的数据通信,实现短信收发与数据传输两大功能。首先要搭建开发平台,在工程中加入需要用到的库函数以及配置文件,然后配置系统时钟、中断控制器、输入输出的GPIO以及相应的串口。在配置这些参数的时候,首先需要对照原理图进行编写,然后查看芯片用到哪些端口和这些端口的作用,这样才能保证无误。接下来就要对各个部分进行配置,以保证模块能够正常运行。

3.1.1 串口配置

开发环境搭建好之后,就可以配置端口参数了。对于本模块,设置USART传输的比特率为9 600 b/s,字长为8 bit,1bit停止位,无检验模式。在对串口1、2、3初始化之后,打开串口的中断响应函数:USART_ITConfig(USART1,USART_IT_RXNE,ENABLE)(以串口1为例),使能相应的串口:USART_Cmd(USART1,ENABLE),这样串口的配置就基本完成了。

3.1.2 中断控制器的配置

首先配置优先级分组,设置先占优先级1位,从优先级3位。本模块定义了5个全局中断,分别为:两个RTC全局中断、USART1全局中断、USART2全局中断、USART3全局中断,分别对每一个中断配置优先级,使能串口再初始化即可。需要注意的是,PC15管脚作为EXTI15的外部中断输入管脚,当该管脚的电平为低电平的时候触发,模块进入短信模式,因此给这个中断一个比较高的优先级,所以定义该中断先占优先级1位,从优先级0位。在设置优先级的时候,必须根据模块的运行情况,选择最优的中断分组和优先级,才能保证程序运行时,能够快速的响应中断。

3.2 无线通信模块短信功能的实现

1) 短信收发具体过程

短信功能的实现主要涉及到两个关键部分,一个是AT命令,另一个是串口的读写。AT命令是主控芯片STM32和SIM900A之间的通信协议,完成对SIM900A的控制。短信的收发主要有两种模式,一种是文本模式,该模式只支持英文内容,另一种是PDU模式。本模块在完成初始化之后,首先通过USART向SIM900A发送“AT+回车”命令来检查AT命令是否正常工作,如果返回OK,则表示能够进行接下来的短信功能。

SIM900A的短信功能主要分为两个部分,第一:读取短消息。读取短消息的设置命令为:AT+CMGR,该命令生效后有两个返回值:index和mode,index就是接收到的短信的编号,我们将接收到的index内容放在一个长度为30的buf中,再将buf写入到串口3中,然后再读取串口3中的内容,这样,短信的编号就获取完毕。获取了短信的编号之后,就能去获取短信的具体内容,而短信的内容则存放在alpha中,它处于响应的第三个位置,通过get_fw(at_string,phnum,19,2)可将短信的内容取出来,这样短信的内容就获取到了。第二:发送短消息。发送短消息的设置命令为:AT+CMGS,首先将发送短信的电话号码存入到一个buf中,将这个buf写入到串口3中,以此获取手机号码。而短信在发送的时候,短信内容之前会带一个“>”号,在程序设计时,只要检测到“>”号时,后面的内容也就是短信发送的内容,最后将短信的内容写到串口3即可。

2)相应的AT命令

3.3 短信命令的定义

本模块自定义了许多短消息命令来设置或者查询模块的参数,具体为:SIP:IP地址设置命令、SPT:端口号设置命令、DID:ID号设置命令、ACON:自动连接命令、PRT:打印命令、LIVE:心跳操作命令、CSQ:信号强度查询命令。这些指令通过短信的方式来实现对模块的操作。

拿SIP来举例,它的实现过程为:首先把短信中的‘=’之前的字符取出来存放在sms_cmd中,然后将sms_cmd与SIP、SIT、DID、ACON、PRT、LIVE、CSQ来比较,如果等于其中一个,则程序跳转到相应的部分来执行相应的操作。这里,strcmp(sms_cmd,“SIP”)==0,程序跳转到SIP部分来执行IP部分的操作。然后,将‘=’之后的字符取出来存放在sms_para中。通过字符串比较函数来判断它是‘?’还是数字。如果是‘?',则表示该短信命令是一个查询命令,就只需要用输出显示IP号给用户看。如果是数字,则表示该短信命令是一个设置命令,就需要重新设置某些参数值,来改变模块的运行过程。对于SIP,如果'=’之后是数字,就表示该短信是用户用来设置模块的IP地址的。此时,就要把当前的IP值赋给模块的IP值就可以了。最后输出显示IP地址设置成功,则SPT部分的操作就完成了。

4 无线通信模块数据传输的实现

4.1 数据格式的定义

数据包的具体格式如下:

数据包头已经定义为S_PACKET_HEADER结构类型,link_id为DWORD类型,对于发送,填写目标link_id,对于接收到的数据包,则为源link_id(由服务器自动转换填写),结束标志为0x01,数据Data则根据具体的命令而各不相同。

4.2 登陆、退出的协议过程

后台软件根据指定的服务器地址和端口号发起TCP连接请求,连接成功后开始命令交互。工作过程描述如下:

1)后台软件首先采用CMD_LOGIN命令,登陆到通信服务器,在登陆成功之前,发送其他任何命令服务器都不处理。发送CMD_LOGIN:FORWARD给服务器,携带数据为S_TERMINAL。该命令的目标link_id设为0,表示发送给服务器,而不是给其他终端。

2)服务器在CMD_LOGIN:RESPONSE_OK中将其他在线模块的信息发送过来,从而在客户端应用程序形成在线模块列表。后台若收到CMD_LO GIN:;RESPONSE_FAILED回应,则失败。收到CMD_LOGIN:RESPONSE_OK回应,则成功,携带数据为S_TERMINAL。

3)后台软件定时发送CMD_KEEP_LIVE命令,以维持链路不被网络和服务器终止。每个在线设备必须在一定的时间间隔内向服务器发送CMD_ KEEP_LIVE信息。服务器将对此作检查,若某设备在一定的时间间隔内没有发送CMD_KEEP_LIVE信息,则认为该设备已经“死亡”,服务器将断开其连接。

4)后台软件可对模块列表中的任意模块进行操控。

5)后台软件退出时,用户设备应首先发送CMD_LOGOFF命令到服务器,告诉服务器“我要退出”,该命令的目标link_id设为0。在发送CMD _LOGOFF:FORWARD给服务器时,不需要携带数据。

图5 登陆,推出的协议过程

4.3 数据传输具体过程

首先要定义建立TCP连接与关闭TCP连接的函数,涉及到的AT指令为AT+CIPSTART和AT+CIPCLOSE。建立TCP连接时,先把AT+CIPSTART指令写到串口3中,然后该指令生效后,会返回一个“CONNECT”,之后只要检测到有返回值“CONNECT”,就表示TCP连接已经建立成功。关闭TCP连接与建立TCP连接类似,AT+CIPCLOSE指令生效后会返回一个“CLOSE OK”,只要检测到“CLOSE OK”,就表示TCP连接已经关闭。

TCP连接建立好以后,透传模式就已经被开启,此时就可以实现数据的发送与读取。

当用户要发送数据时,就必须根据数据包的结构来发送数据,首先把包头写入到串口3中,如果有数据,则把数据内容和接收对象一并写入到串口3中,这样数据的发送就完成了。

读取数据时,只要有数据过来,就一次一个字节,把数据存放到user_string[i]中,然后i++,再次接收数据。

至于数据读取到什么时候结束,本程序定义了3种结束的情况:1)如果用户定义了数据的长度,就读取到最后一个字节才结束。2)如果用户没有定义数据的长度,就在读取到回车符的时候结束。3)如果遇到既没有已定义的数据包长度,又没有读取到回车符的情况时,就在数据存放的长度超过user_string[i]总长度的四分之三的时候结束,或者在超过100毫秒没有数据发过来的时候结束数据的读取。

4.4 相应的AT命令

5、断线重连的处理

5.1 TCP链接的关闭和建立

TCP链接的建立涉及到的AT命令是at+cipstart,该指令有两个返回值,分别是模块的IP地址和端口号。首先将at+cipstart指令返回的当前模块的IP地址和端口号存放到一个buf中,然后将这个buf写入到串口3中,如果之后能够读取到返回值“CONNECT”,就表示TCP链接已经建立好。

TCP链接的关闭涉及到的AT命令是at+cipclose,该指令没有返回值,可以直接将该指令写到串口3中,如果检测到“CLOSE OK”,就表示TCP链接已经被关闭。

5.2 DCD的检测

当TCP建立起来之后,DCD引脚的电压值便由高电平变为低电平,因此DCD引脚的电平状态可以用过来指示TCP的连接情况。在程序中,本文设置时钟在检测DCD引脚的状态值,当该引脚的电平值由高电平变为低电平时,就表示模块模块已经掉线,然后模块就重新开始登陆,直到登陆上为止。

5.3 TCP链接下的短信收发

本文定义了一个全局中断,当有短信或者电话到达模块时,RI引脚的电平便会由高电平变为低电平,此时便会触发全局中断,模块立即转而处理短信收发或者数据传输。

6、测试效果

本模块在设计完成之后,在实验室稳定运行了2周时间,没有出现什么问题。然后又进行了特殊情况的测试,在模块断电后恢复供电,数据中心断电后重新上电的情况下,都能够重新连接上线,并且能够继续稳定运行。随后模块又被送到厂方使用,在被使用了3周之后,厂方反应模块运行情况良好,没有掉线的情况。

7、结论

本文设计的无线通信模块,是利用STM32来控制SIM900A芯片,来实现短消息的收发与数据的无线传输。本设计完成了无线通信的硬件部分和软件部分的设计与实现。在多次运行试验时,本模块没有出现掉线以及发热等问题,非常稳定。本次设计成本较低,运行稳定可靠,应用范围十分广泛,利用SIM900A,降低了模块的成本,使其更加具有商业价值。

作者:霍涛,贾振堂,上海电力学院电子与信息工程
来源:
面包板

围观 609

页面

订阅 RSS - STM32