標(biāo)題: 通過上位機(jī)輸入產(chǎn)生PWM控制LED燈實(shí)驗(yàn) [打印本頁]
作者: WIZnet-Ian 時(shí)間: 2017-9-5 09:45
標(biāo)題: 通過上位機(jī)輸入產(chǎn)生PWM控制LED燈實(shí)驗(yàn)
1 實(shí)驗(yàn)?zāi)康?/font>上位機(jī)通過串口發(fā)送格式為:“redbrightness,greenbrightness,bluebrightness”的字符串到MCU。MCU將數(shù)字轉(zhuǎn)化成相應(yīng)的亮度。
2 實(shí)驗(yàn)總體設(shè)計(jì)實(shí)驗(yàn)主要分兩個(gè)部分:PWM配置以及串口通信配置。整個(gè)實(shí)驗(yàn)的難點(diǎn)在于ASCII碼轉(zhuǎn)換為數(shù)字的過程。
3 PWM產(chǎn)生原理通用定時(shí)器可以利用GPIO引腳進(jìn)行脈沖輸出。要使STM32的通用定時(shí)器TIMx產(chǎn)生PWM輸出,需要用到3個(gè)寄存器。分別是:捕獲/比較模式寄存器(TIMx_CCMR1/2)、捕獲/比較使能寄存器(TIMx_CCER)、捕獲/比較寄存器(TIMx_CCR1~4)。(注意,還有個(gè)TIMx的ARR寄存器是用來控制pwm的輸出頻率)。
對(duì)于捕獲/比較模式寄存器(TIMx_CCMR1/2),該寄存器總共有2個(gè),TIMx _CCMR1和TIMx_CCMR2。TIMx_CCMR1控制CH1和2,而TIMx_CCMR2控制CH3和4。
其次是捕獲/比較使能寄存器(TIMx_CCER),該寄存器控制著各個(gè)輸入輸出通道的開關(guān)。
最后是捕獲/比較寄存器(TIMx_CCR1~4),該寄存器總共有4個(gè),對(duì)應(yīng)4個(gè)輸通道CH1~4。4個(gè)寄存器作用相近,都是用來設(shè)置pwm的占空比的。
例如,若配置脈沖計(jì)數(shù)器TIMx_CNT為向上計(jì)數(shù),而重載寄存器TIMx_ARR被配置為N,即TIMx_CNT的當(dāng)前計(jì)數(shù)值數(shù)值X在TIMxCLK時(shí)鐘源的驅(qū)動(dòng)下不斷累加,當(dāng)TIMx_CNT的數(shù)值X大于N時(shí),會(huì)重置TIMx_CNT數(shù)值為0重新計(jì)數(shù)。
而在TIMxCNT計(jì)數(shù)的同時(shí),TIMxCNT的計(jì)數(shù)值X會(huì)與比較寄存器TIMx_CCR預(yù)先存儲(chǔ)了的數(shù)值A(chǔ)進(jìn)行比較,當(dāng)脈沖計(jì)數(shù)器TIMx_CNT的數(shù)值X小于比較寄存器TIMx_CCR的值A(chǔ)時(shí),輸出高電平(或低電平),相反地,當(dāng)脈沖計(jì)數(shù)器的數(shù)值X大于或等于比較寄存器的值A(chǔ)時(shí),輸出低電平(或高電平)。
如此循環(huán),得到的輸出脈沖周期就為重載寄存器TIMx_ARR存儲(chǔ)的數(shù)值(N+1)乘以觸發(fā)脈沖的時(shí)鐘周期,其脈沖寬度則為比較寄存器TIMx_CCR的值A(chǔ)乘以觸發(fā)脈沖的時(shí)鐘周期,即輸出PWM的占空比為A/(N+1) 。
4 PWM配置步驟4.1 配置GPIOvoid LED_Config(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);//開啟復(fù)用時(shí)鐘
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 配置定時(shí)器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 配置PWMvoid 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 小結(jié)PWM模式1:
在向上計(jì)數(shù)時(shí),一旦TIMx_CNT<TIMx_CCR1時(shí)通道1為有效電平,否則為無效電平;在向下計(jì)數(shù)時(shí),一旦TIMx_CNT>TIMx_CCR1時(shí)通道1為無效電平(OC1REF=0),否則為有效電平(OC1REF=1)。
PWM模式2:
在向上計(jì)數(shù)時(shí),一旦TIMx_CNT<TIMx_CCR1時(shí)通道1為無效電平,否則為有效電平;在向下計(jì)數(shù)時(shí),一旦TIMx_CNT>TIMx_CCR1時(shí)通道1為有效電平,否則為無效電平。
同時(shí)輸出的有效點(diǎn)評(píng)還與極性配置有關(guān):
TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High;
此配置是高電平為有效電平,反之亦然。
5 UART配置步驟5.1 配置UART1以及對(duì)應(yīng)的GPIOvoid 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 初始化設(shè)置*/
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 中斷函數(shù)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; //計(jì)數(shù)清零
IDLE_Flag= 1; //標(biāo)記接收到一幀的數(shù)據(jù)
}
}
5.4 小結(jié)STM32單片機(jī)可以實(shí)現(xiàn)接收不定長度字節(jié)數(shù)據(jù)。由于STM32單片機(jī)帶IDLE中斷,利用這個(gè)中斷,可以接收不定長字節(jié)的數(shù)據(jù)。由于STM32屬于ARM單片機(jī),所以這篇文章的方法也適合其他的ARM單片機(jī)。
IDLE就是串口收到一幀數(shù)據(jù)后,發(fā)生的中斷。比如說給單片機(jī)一次發(fā)來1個(gè)字節(jié),或者一次發(fā)來8個(gè)字節(jié),這些一次發(fā)來的數(shù)據(jù),就稱為一幀數(shù)據(jù),也可以叫做一包數(shù)據(jù)。 一幀數(shù)據(jù)結(jié)束后,就會(huì)產(chǎn)生IDLE中斷。這個(gè)中斷十分有用,可以省去了好多判斷的麻煩。
6 ASCII碼轉(zhuǎn)換為數(shù)字6.1 實(shí)現(xiàn)步驟:
/*讀取第1部分?jǐn)?shù)值 */
while(RxBuffer[ i]!= ','){ i++; len++;}//如果不為','長度加1
for(j=i-len;j<i; j++){
value= RxBuffer[j]&0x0f; //將ascii碼轉(zhuǎn)換為數(shù)字
pwm_red+= value * Power(len-1);
len--;
}
i++;
len= 0;
/*讀取第2部分?jǐn)?shù)值 */
while(RxBuffer[ i]!= ','){ i++; len++;}
for(j=i-len;j<i; j++){
value= RxBuffer[j]&0x0f; //將ascii碼轉(zhuǎn)換為數(shù)字
pwm_green+= value * Power(len-1);
len--;
}
i++;
len= 0;
/*讀取第3部分?jǐn)?shù)值 */
while(RxBuffer[ i] != '\0'){ i++; len++;}
for(j=i-len;j<i; j++){
value= RxBuffer[j]&0x0f; //將ascii碼轉(zhuǎn)換為數(shù)字
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;//清除數(shù)組
i= 0;
len= 0;
}
}
}
6.2 10的n次方函數(shù)uint8_t Power(uint8_t pow)
{
uint8_ti;
uint8_tsum = 1;
for(i=0;i<pow; i++) sum *= 10;
returnsum;
}
作者: skyweb88 時(shí)間: 2017-9-5 21:29
方法不錯(cuò),也可以用紅外遙控之類的來進(jìn)行數(shù)字輸出,調(diào)出想要的PWM波型,驅(qū)動(dòng)不同亮度的LED燈。
| 歡迎光臨 (http://www.raoushi.com/bbs/) |
Powered by Discuz! X3.1 |