历史上的今天
返回首页

历史上的今天

今天是:2024年11月11日(星期一)

正在发生

2020年11月11日 | STM32单片机学习---PWM输出

2020-11-11 来源:eefocus

实现功能:采用定时器2的通道2,使PA1输出频率1K,占空比40的PWM波形,用PA8随意延时取反led灯,指示程序运行。


首先熟悉一下定时器的PWM相关部分。


其实PWM就是定时器的一个比较功能而已。

CNT里的值不断++,一旦加到与CCRX寄存器值相等,那么就产生相应的动作。这点和AVR单片机很类似。既然这样,我们要产生需要的PWM信号,就需要设定PWM的频率和PWM的占空比。


首先说频率的确定。由于通用定时器的时钟来源是PCLK1,而我又喜欢用固件库的默认设置,那么定时器的时钟频率就这样来确定了,如下:


AHB(72MHz)→APB1分频器(默认2)→APB1时钟信号(36MHz)→倍频器(*2倍)→通用定时器时钟信号(72MHz)。

这里为什么是这样,在RCC模块学习记录里有详细记载,不多说。

因此图中的CK_PSC就是72MHz了。


下面的资料也是网上一搜一大把,我就罗列了:

STM32的PWM输出有两种模式,模式1(PWM1)和模式2(PWM2),由TIMx_CCMRx寄存器中的OCxM位确定的(“110”为模式1,“111”为模式2)。模式1和模式2的区别如下:

110:PWM模式1-在向上计数时,一旦TIMx_CNT=TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。

111:PWM模式2-在向上计数时,一旦TIMx_CNT=TIMx_CCR1时通道1为有效电平,否则为无效电平。

由此看来,模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。我用的是模式一,因此后面的设定都是按照模式一来设定的。

 

PWM的周期是就是由定时器的自动重装值和CNT计数频率决定的。而CNT的计数时钟是CK_PSC经分频器PSC得到,因此CNT的时钟就是CK_PSC/分频系数。这个分频系数在TIM_TimeBaseStructure.TIM_Prescaler确定。我设置的值是72,因此CNT的计数频率也就是CK_CNT的频率为1MHz。


下一步就是确定定时器自动重装值。因为CNT每自加到ARR寄存器的值时就会自动清零,当然前提是设定为为向上计数模式,而就是根据这个溢出事件来改变PWM的周期。所以PWM信号的频率由ARR的值来确定。我设置的值是1000-1,即TIM_TimeBaseStructure.TIM_Period = 1000-1;因此PWM的周期是1MHz/1000=1KHz。


接下来就要确定PWM的占空比了。因为CNT在自加到ARR值的过程中会不断和CRRX的值相比较,一旦二者相等就产生匹配事件,但要注意CNT不会理会这件事,它会继续++直到等于ARR。而CRRX的值我设定为400-1,那么占空比就随之确定为40%。


好了,下面就是库函数的配置了。


TIMER输出PWM实现步骤

1.       设置RCC时钟;

2.       设置GPIO;

3.       设置TIMx定时器的相关寄存器;

4.       设置TIMx定时器的PWM相关寄存器。

 

首先是main函数和全局变量申明,很简单,不作说明


GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TimOCInitStructure;

int main(void)
{
      

 

 

      rcc_cfg();
      gpio_cfg();
      tim2_cfg();
      pwm_cfg();
// 
  while (1)
  {
   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
 
 delay();

   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
 
    delay();
  }
}

 

下面是IO口的配置:

void gpio_cfg()
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

此处要注意的是PWM输出口要配置为复用推挽输出,原因我也不知道,反正照搬就是了。

 

下面是TIM配置函数,注释很清楚了,不作说明:

void tim2_cfg()
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

  TIM_DeInit(TIM2);
  TIM_InternalClockConfig(TIM2);
  //预分频系数为72,这样计数器时钟为72MHz/72 = 1MHz
  TIM_TimeBaseStructure.TIM_Prescaler = 72;
  //设置时钟分割
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  //设置计数器模式为向上计数模式
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  //设置计数溢出大小,每计1000个数就产生一个更新事件
  TIM_TimeBaseStructure.TIM_Period = 1000-1;
  //将配置应用到TIM2中
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

  //禁止ARR预装载缓冲器
  TIM_ARRPreloadConfig(TIM2, DISABLE);
 
  TIM_Cmd(TIM2, ENABLE);  //使能TIMx外设
}


接下来是关键的PWM的配置函数:

void pwm_cfg()

{

      //设置缺省值

       TIM_OCStructInit(&TimOCInitStructure);

       //PWM模式1输出

       TimOCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

       //设置占空比,占空比=(CCRx/ARR)*100%或(TIM_Pulse/TIM_Period)*100%

       TimOCInitStructure.TIM_Pulse = 400-1;

       //TIM输出比较极性高

       TimOCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

       //使能输出状态

       TimOCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

       //TIM2的CH2输出

       TIM_OC2Init(TIM2, &TimOCInitStructure);

       //设置TIM2的PWM输出为使能

       TIM_CtrlPWMOutputs(TIM2,ENABLE);

}

stm32固件库的输出比较单元结构体与定时器的时基单元是分开定义的,而PWM模式只是输出比较结构体成员TimOCInitStructure.TIM_OCMode的一个取值,当把此结构体填充完后,还要映射到某个定时器,用TIM_OCXInit函数实现,我用了一个X,说明不止一个这样的函数,事实上,stm32的通用定时器都有四个通道,每个通道对应一个初始化函数,这里真够纠结的!最后还要使能该定时器的PWM输出功能,

TIM_CtrlPWMOutputs(TIM2,ENABLE)函数要注意,是outputs而不是output,说明TIM2不止一个通道嘛!够复杂,够繁琐的!


下面是输出比较单元的结构体原型:

typedef struct
{
  uint16_t TIM_OCMode;       

  uint16_t TIM_OutputState;  

  uint16_t TIM_OutputNState; 

  uint16_t TIM_Pulse;        

  uint16_t TIM_OCPolarity;   

  uint16_t TIM_OCNPolarity;  

  uint16_t TIM_OCIdleState;  

  uint16_t TIM_OCNIdleState; 
} TIM_OCInitTypeDef;

其中没有加色的成员是高级定时器才有的,通用定时器就不用管了。

这里还有个TimOCInitStructure.TIM_OCPolarity 成员需要注意,它有什么作用呢?在网上查的资料,


前面说到pwm有pwm1和pwm2两种模式,这两种模式只能控制到OCXREF为止,TIM_OCPolarity 能控制OC1是直接等于OCXREF,还是取反极性!OC1才是最终的PWM信号。


这里有个小插曲,我用示波器去测量PWM信号,发现信号居然是双极性的,然后改变TIM_OCPolarity ,再测,还是双极性,只是倒了个跟头。还真以为stm32单片机能输出两极性的PWM,后面把示波器改为直流档(之前用的是交流档),波形才从零电位一下纵向移上去。以后要注意!

推荐阅读

史海拾趣

Apx-Crystal公司的发展小趣事

面对日益激烈的市场竞争,Apx-Crystal公司意识到单凭自身的力量难以保持长期的竞争优势。于是,公司积极寻求与行业内外的合作伙伴进行技术交流和合作研发。通过与知名高校、科研机构以及同行的紧密合作,Apx-Crystal成功开发出了一系列具有创新性和领先性的电子产品,进一步巩固了其在行业内的领先地位。同时,公司还加大了对产业链的整合力度,推动了产业升级和可持续发展。

Esterline Power Systems公司的发展小趣事

Esterline Power Systems深知人才是企业发展的核心动力。因此,公司高度重视人才培养和团队建设。公司建立了完善的培训体系,为员工提供多元化的培训和发展机会。同时,公司还注重营造良好的企业文化氛围,鼓励员工积极参与团队合作和创新实践。这些举措不仅提高了员工的专业技能和综合素质,也增强了公司的凝聚力和竞争力。

APTA Group Inc公司的发展小趣事

在电子行业的发展过程中,APTA Group Inc也遭遇了不少挑战。例如,原材料价格的波动、国际贸易环境的变化等都曾给公司带来不小的压力。然而,APTA并未被这些困难击垮,而是积极调整战略,优化生产流程,降低成本,提高产品质量。这种在挑战中展现出的韧性和应变能力,使得APTA能够在逆境中保持稳健的发展态势。

BETA Transformer Technology Corp公司的发展小趣事

随着电子行业的快速发展和变革,BETA Transformer Technology Corp公司面临着前所未有的挑战。为了应对这些挑战,公司不断加大研发投入,加强技术创新,推出了一系列符合市场需求的新产品。同时,BETA公司还积极调整生产结构,优化供应链管理,降低成本,提高产品质量。这些举措使得BETA公司在激烈的市场竞争中保持了领先地位。

地博电子(DIBO)公司的发展小趣事

近年来,地博电子积极响应行业发展趋势,不断推进数字化转型和精益生产。在2022年,公司导入了精益生产系统,通过优化生产流程、降低库存和浪费等措施,进一步提高了生产效率和产品质量。同时,在2023年,地博电子还导入了OA/SAP信息化系统,开启了数字化转型之路。这些举措使得地博电子在电子材料行业中保持了领先地位,并为公司的未来发展奠定了坚实基础。

请注意,以上故事概要仅为地博电子(DIBO)公司发展历程中的部分关键事实,更多详细信息和数据可参考公司官方资料。

智烽维(CDA)公司的发展小趣事

智烽维(CDA)于2007年成立,专注于超级电容器(法拉电容)的研发和生产。在创立初期,公司面临着资金短缺、技术挑战和市场认知度低等多重困难。然而,凭借对技术的执着追求和对市场的敏锐洞察,智烽维成功推出了卷绕型超级电容器产品,并逐渐在市场上建立起了一定的知名度。

问答坊 | AI 解惑

自适应滤波的verilog程序

自适应滤波的verilog程序求助,最近在做自适应滤波lms算法的verilog编程,我在数字信号处理的FPGA实现(第二版)中看到有相关的程序,但为什么仿真的输出都是不定值呢,权系数f0,f1就是不定值,mult是用quartus产生的ip核产生的,谁能帮我看一下啦 ...…

查看全部问答>

请教MAX7219驱动8个LED显示全亮的问题!

关于max7219的显示 我是电子爱好者,近日有个MAX7219的片子,用89C2051 想编个程序显示看看,结果不可以,我感觉没有问题,但我要显示的8个led一上电就全亮,再就没有熄灭过,怎么回事呢,软件问题还是硬件问题,百思不得其解,请问哪位大虾给各指 ...…

查看全部问答>

activesync连接和断开的监测

现在要在wince6.0的某个驱动监测activesync的连接和断开状态,比如连接好了,注册表哪里可以有这个标志位,断开同样。 我查了很多地方,包括activesync manager的代码,都是在窗口应用程序里监测消息。驱动里不能有窗口消息,所以请各位大虾给我一 ...…

查看全部问答>

wince6 0下怎么添加.net framework 3.5

由于我需要LBE游戏助手~但是现有WINCE6.0不支持,提示.NET版本低 wince6 0下怎么添加.net framework 3.5 开发环境:VS2005+PB6.0, 原先自带的是./NET2.0   各位大哥~help me ~~~~~最好别用VS2008~这个我电脑上没有…

查看全部问答>

请问:这个hex文件的内容怎么理解?

如题: :200000000265A002720322FFFFFFFF028157FFFFFFFFFF02743812851BAE07E4FF128829B4 :20002000AF0622023D677427252AF582E43401F583E0FF74E2252AF582E43402F583EFF0E5 :20004000052AE52A2274272FF582E43401F583E0FE74072FF582E43420F58322AB2 ...…

查看全部问答>

想买个ARM开发板,请给我点建议

  我现在51单片机学的差不多了,我的专业是电子信息科学与技术,我想以后往嵌入式开发方向发展。   想先自学ARM,所以想买个板子。但是不大清楚arm7,arm9之类的。   希望有过经历的学长给我点建议,大概买什么 ...…

查看全部问答>

问一个关于AD的问题

我把一个正弦波转成数字信号存在存储器里,在读出来的时候两个波形总是不重合。有相位差怎么解决?…

查看全部问答>

2011高交会,看中日厂商的生存之道

图1.深圳高交会2号电子馆全貌   “如果你是幸运的,你只需要一种道德而不要贪多,这样你过桥会更容易些。”                      ...…

查看全部问答>

zigbee无线无线数据发送调试成功。

经过这两天调试,zigbee模块可以使用了。 前天收到PCB的板子上,经过这两天的调试,可以数据发送了(也应该可以接收)。本来最担心的也就是无线这块,下一步就是测试通信距离和串口数据发送通信了。 1:zigbee无线子模块PCB; 2:zigbee调试PC ...…

查看全部问答>