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 配置GPIOvoid 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 配置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 小結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以及對應的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 初始化設置*/ 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 10的n次方函數uint8_t Power(uint8_t pow) { uint8_ti; uint8_tsum = 1;
for(i=0;i<pow; i++) sum *= 10;
returnsum; } |