找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5383|回复: 1
打印 上一主题 下一主题
收起左侧

通过上位机输入产生PWM控制LED灯实验

[复制链接]
跳转到指定楼层
楼主
ID:231047 发表于 2017-9-5 09:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1        实验目的
上位机通过串口发送格式为:“redbrightness,greenbrightness,bluebrightness”的字符串到MCU。MCU将数字转化成相应的亮度。
2        实验总体设计
实验主要分两个部分:PWM配置以及串口通信配置。整个实验的难点在于ASCII码转换为数字的过程。
3        PWM产生原理
通用定时器可以利用GPIO引脚进行脉冲输出。要使STM32的通用定时器TIMx产生PWM输出,需要用到3个寄存器。分别是:捕获/比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。(注意,还有个TIMx的ARR寄存器是用来控制pwm的输出频率)。
对于捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器总共有2个,TIMx _CCMR1和TIMx_CCMR2。TIMx_CCMR1控制CH1和2,而TIMx_CCMR2控制CH3和4。
其次是捕获/比较使能寄存器(TIMx_CCER),该寄存器控制着各个输入输出通道的开关。
最后是捕获/比较寄存器(TIMx_CCR1~4),该寄存器总共有4个,对应4个输通道CH1~4。4个寄存器作用相近,都是用来设置pwm的占空比的。
例如,若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR被配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。
而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。
如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1) 。
4        PWM配置步骤4.1         配置GPIO
void LED_Config(void)
{
         GPIO_InitTypeDefGPIO_InitStructure;

         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);//开启复用时钟

         GPIO_InitStructure.GPIO_Pin=  LED_RED| LED_BLUE | LED_GREEN;
    GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
         GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
         GPIO_Init(GPIOC,&GPIO_InitStructure);

         GPIO_SetBits(GPIOC,LED_RED | LED_BLUE | LED_GREEN);
}
4.2         配置定时器
void TIMER_Config(void)
{
         TIM_TimeBaseInitTypeDef     TIM_BaseInitStructure;
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

         GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);

         TIM_BaseInitStructure.TIM_Period= 255;
         TIM_BaseInitStructure.TIM_Prescaler= 0;
         TIM_BaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;
         TIM_BaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;

         TIM_TimeBaseInit(TIM3,&TIM_BaseInitStructure);
         TIM_ARRPreloadConfig(TIM3,ENABLE);
         TIM_Cmd(TIM3,ENABLE);
}
4.3         配置PWM
void PWM_Config(void)
{
         TIM_OCInitTypeDef  TIM_OCInitStructure;

         TIM_OCStructInit(&TIM_OCInitStructure);   
         TIM_OCInitStructure.TIM_Pulse= 0;   
         TIM_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1;                                                                       //选择模式1
         TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable;  
         TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_Low      //极性为高电平有效

    TIM_OC2Init(TIM3, &TIM_OCInitStructure);
         TIM_OC3Init(TIM3,&TIM_OCInitStructure);
         TIM_OC4Init(TIM3,&TIM_OCInitStructure);         

         TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
         TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
         TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);

TIM_CtrlPWMOutputs(TIM3,ENABLE);   
}
4.4         小结
PWM模式1:
在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
PWM模式2:
在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。
同时输出的有效点评还与极性配置有关:
TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High;
此配置是高电平为有效电平,反之亦然。
5        UART配置步骤5.1         配置UART1以及对应的GPIO
void Usart_Config(uint32_t BaudRate)
{
     GPIO_InitTypeDef GPIO_InitStructure;
     USART_InitTypeDef USART_InitStructure;

     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);   
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
     GPIO_Init(GPIOA, &GPIO_InitStructure);

     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
     GPIO_Init(GPIOA, &GPIO_InitStructure);

     /*USART 初始化设置*/
     USART_InitStructure.USART_BaudRate = BaudRate;        
     USART_InitStructure.USART_WordLength = USART_WordLength_8b;  
     USART_InitStructure.USART_StopBits = USART_StopBits_1;         
     USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   

     USART_Init(USART_PC, &USART_InitStructure);

     USART_ITConfig(USART_PC, USART_IT_RXNE, ENABLE);  //开启串口接收中断
           USART_ITConfig(USART_PC, USART_IT_IDLE,ENABLE);  //开启串口接收中断

     USART_Cmd(USART_PC, ENABLE);
}
5.2         配置中断
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

  /*Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

  /*Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
5.3         中断函数
void USART1_IRQHandler(void)
{
uint8_t clear = clear;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {
                   USART_ClearITPendingBit(USART1,USART_IT_RXNE);
   /* Read one byte from the receive data register */
   RxBuffer[RxCounter++] = USART_ReceiveData(USART1);
  }
         elseif(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
         {
                   clear= USART1->SR;
                   clear= USART1->DR;                     //先读SR再读DR,为了清除IDLE中断
                   RxNumber= RxCounter;
                   RxCounter= 0;                                               //计数清零
                   IDLE_Flag= 1;                                       //标记接收到一帧的数据
         }
}
5.4         小结
STM32单片机可以实现接收不定长度字节数据。由于STM32单片机带IDLE中断,利用这个中断,可以接收不定长字节的数据。由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的ARM单片机。
IDLE就是串口收到一帧数据后,发生的中断。比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。 一帧数据结束后,就会产生IDLE中断。这个中断十分有用,可以省去了好多判断的麻烦。
6        ASCII码转换为数字6.1         实现步骤:
                            /*读取第1部分数值 */
                            while(RxBuffer[ i]!=  ','){         i++;  len++;}//如果不为','长度加1

                            for(j=i-len;j<i; j++){
                                     value= RxBuffer[j]&0x0f;         //ascii码转换为数字
                                     pwm_red+= value * Power(len-1);
                                     len--;
                            }

                            i++;
                            len= 0;

                            /*读取第2部分数值 */
                            while(RxBuffer[ i]!=  ','){         i++;  len++;}
                            for(j=i-len;j<i; j++){
                                     value= RxBuffer[j]&0x0f;         //ascii码转换为数字
                                     pwm_green+= value * Power(len-1);
                                     len--;
                            }                           
                            i++;  
                            len= 0;
                            /*读取第3部分数值 */
                            while(RxBuffer[ i] !=  '\0'){      i++;  len++;}
                            for(j=i-len;j<i; j++){
                                     value= RxBuffer[j]&0x0f;         //ascii码转换为数字
                                     pwm_blue+= value * Power(len-1);
                                     len--;
                            }
                            RedOutput(pwm_red);
                            GreenOutput(pwm_green);
                            BlueOutput(pwm_blue);
                            pwm_red= 0;
                            pwm_green= 0;
                            pwm_blue= 0;
                            for(i=0;i<11; i++)               RxBuffer[ i] =NULL;//清除数组
                            i= 0;
                            len= 0;
                   }
         }
}
6.2         10n次方函数
uint8_t Power(uint8_t pow)
{
         uint8_ti;
         uint8_tsum = 1;

         for(i=0;i<pow; i++)   sum *= 10;

         returnsum;
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:231256 发表于 2017-9-5 21:29 | 只看该作者
方法不错,也可以用红外遥控之类的来进行数字输出,调出想要的PWM波型,驱动不同亮度的LED灯。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表