目錄
- 1、準(zhǔn)備工作
- 2、思路分析
- 3、實際操作
- 4、小結(jié)
1、準(zhǔn)備工作
1.首先我們需要準(zhǔn)備32的最小系統(tǒng)板或者開發(fā)板。
2.準(zhǔn)備一個LED燈(如果使用板子上的燈來實現(xiàn)則不需要,下面我是使用最小系統(tǒng)板上的LED燈來實現(xiàn))。
3.若干杜邦線。
4.軟件方面的準(zhǔn)備,我是直接使用開源PWM源碼進(jìn)行修改。
2、思路分析
一、使用串口調(diào)試助手向單片機發(fā)送數(shù)據(jù)(這個數(shù)據(jù)可以是一個字符,也可以是字符串,根據(jù)個人需求),我們發(fā)送的數(shù)據(jù)被單片機接收到后,會被保存在數(shù)據(jù)緩沖區(qū)USART_RX_BUF這個函數(shù)中。
二、我們的數(shù)據(jù)是存在USART_RX_BUF函數(shù)中,只要我們對USART_RX_BUF函數(shù)中的數(shù)據(jù)進(jìn)行判斷就可以讓它實現(xiàn)不同的功能,這個判斷可以按位操作,也可以使用數(shù)組的方式進(jìn)行判斷。
三、主函數(shù)中寫入我們需要實現(xiàn)的功能函數(shù),主要使用IF判斷語句,來進(jìn)行判斷。
下面來看看實際操作。
3、實際操作
1)如果你也是使用開源的PWM模板的話,第一步就可以省略了,第一步主要做一些使能串口和定義串口,定時器等的工作,我這里我使用的是定時器3的通道2——PB5(部分重映射,因為最小系統(tǒng)板的LED燈是對應(yīng)PC13口的,到時候看效果還要使用一根杜邦線把PB5和PC13連在一起。如果自己準(zhǔn)備了LED的小伙伴也可以直接接自己的LED但是最好要接一個保護(hù)電阻,還有要與單片機共地哦)這些都是開源模板里面已經(jīng)幫我們定義好的,我們直接使用就行。如果是想自己寫的小伙伴開源參考下面的代碼 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定時器3時鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外設(shè)和AFIO復(fù)用功能模塊時鐘 GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //設(shè)置該引腳為復(fù)用輸出功能,輸出TIM3 CH2的PWM脈沖波形 GPIOB.5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO //初始化TIM3 TIM_TimeBaseStructure.TIM_Period = arr; //設(shè)置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //設(shè)置用來作為TIMx時鐘頻率除數(shù)的預(yù)分頻值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時鐘分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數(shù)模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據(jù)TIM_TimeBaseInitStruct中指定的參數(shù)初始化TIMx的時間基數(shù)單位 //初始化TIM3 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈沖寬度調(diào)制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高 TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根據(jù)T指定的參數(shù)初始化外設(shè)TIM3 OC2 TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的預(yù)裝載寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIM3 上面這串代碼就是使能了定時器3的通道2 ,和配置了相關(guān)的GPIO口。這就完成了第一步。
2)使能串口和配置串口,USART1_TX --GPIOA.9(發(fā)送);USART1_RX—GPIOA.10(接收),串口1的發(fā)生和接收分別對應(yīng)著PA9和PA10,所以我們要使能和配置這兩個口,把PA9配置成輸出口,PA10配置成輸入口。然后還要使能中斷,其實在這個項目中,中斷不是必要的 ,但是最好也要搞一下。還要寫中斷服務(wù)函數(shù),根據(jù)自己需要寫,我這里我只是把它用作了判斷數(shù)據(jù)是否接收成功。如果對應(yīng)串口這個不是很了解的,也可以看我上一篇文章,是介紹串口和串口中斷的。分析到這些就OK了,下面上代碼。
- <font face="-apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif"><font color="#4d4d4d"> GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘
-
- //USART1_TX GPIOA.9
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
-
- //USART1_RX GPIOA.10初始化
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
-
- //Usart1 NVIC 配置
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優(yōu)先級3
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優(yōu)先級3
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根據(jù)指定的參數(shù)初始化VIC寄存器
-
- //USART 初始化設(shè)置
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
- USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷
- USART_Cmd(USART1, ENABLE); //使能串口1 </font></font>
復(fù)制代碼 上面這些是串口的基本配置,下面是中斷服務(wù)函數(shù)
- void USART1_IRQHandler(void) //串口1中斷服務(wù)程序
- {
- u8 Res;
- #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.
- OSIntEnter();
- #endif
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數(shù)據(jù)必須是0x0d 0x0a結(jié)尾)
- {
- Res =USART_ReceiveData(USART1); //讀取接收到的數(shù)據(jù)
-
- if((USART_RX_STA&0x8000)==0)//接收未完成
- {
- if(USART_RX_STA&0x4000)//接收到了0x0d
- {
- if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
- else USART_RX_STA|=0x8000; //接收完成了
- }
- else //還沒收到0X0D
- {
- if(Res==0x0d)USART_RX_STA|=0x4000;
- else
- {
- USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
- USART_RX_STA++;
- if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數(shù)據(jù)錯誤,重新開始接收
- }
- }
- }
- }
- #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.
- OSIntExit();
- #endif
- }
復(fù)制代碼 如果想要主函數(shù)中比較簡潔的話,也可以把判斷的代碼放到中斷服務(wù)函數(shù)里面來,每次我們從串口發(fā)送一個數(shù)據(jù)過來,如果你寫了中斷的話,它都會進(jìn)行中斷服務(wù)函數(shù)中的。
3)這個也是最重要的一步,前兩步在源碼中都有的,只要你根據(jù)你需要改就行。這步我們說如何控制LED的閃爍或者是呼吸的效果。我使用的是一個位一個位的判斷,這樣子比較的燒芯片,但是我當(dāng)時想到的是這個辦法,后面我又知道可以使用數(shù)組進(jìn)行判斷,這個數(shù)組函數(shù)是C語言中的,感興趣的小伙伴可以去查查,我這里主要講燒芯片的辦法,首先我先判斷串口調(diào)試助手發(fā)送進(jìn)來的是不是“huxi”這個數(shù)據(jù),如果是我就會令一個變量,這里是t,t=1,這樣后面我們就可以直接判斷t是否等于1來判斷要不要實現(xiàn)呼吸這個效果了,后面需要清除接收標(biāo)記 USART_RX_STA=0;這樣之后串口才能重新接收數(shù)據(jù)。
- if(USART_RX_BUF[0]=='h'&&USART_RX_BUF[1]=='u'&&USART_RX_BUF[2]=='x'
- &&USART_RX_BUF[3]=='i')
- {
-
- t=1;
- USART_RX_STA=0;
- // printf("t2.txt=\"呼吸\"\xff\xff\xff");
- }
復(fù)制代碼- if(t==1)
- {
- delay_ms(10);//去抖動
- if(dir)led0pwmval++;
- else led0pwmval--;
- if(led0pwmval>200)dir=0;
- if(led0pwmval==0)dir=1;
- TIM_SetCompare2(TIM3,led0pwmval);
-
- }
復(fù)制代碼 上面這兩個代碼就是實現(xiàn)呼吸燈效果的,閃爍效果的做法跟呼吸燈是一樣的,也是先進(jìn)行判斷,然后調(diào)用判斷結(jié)果,我這里是判斷接收是否等于“shanshuo”這個數(shù)據(jù),如果等于t=0,后面調(diào)用t這個變量就可以了,話不多說,上代碼。- else if(USART_RX_BUF[0]=='s'&&USART_RX_BUF[1]=='h'&&USART_RX_BUF[2]=='a'
- &&USART_RX_BUF[3]=='n'&&USART_RX_BUF[4]=='s'&&USART_RX_BUF[5]=='h'&&USART_RX_BUF[6]=='u'&&USART_RX_BUF[7]=='o')
- {
-
- t=0;
- USART_RX_STA=0;
-
- }
復(fù)制代碼- if(t==0)
- {
- TIM_SetCompare2(TIM3,0);
- delay_ms(300);
- TIM_SetCompare2(TIM3,899);
- delay_ms(300);
-
- }
復(fù)制代碼 這樣使用兩次判斷就可以把這兩個功能都實現(xiàn)了。不過有一個小問題是,我們這樣子接收判斷是把原來存在數(shù)據(jù)緩沖區(qū)USART_RX_BUF中的數(shù)據(jù)給覆蓋掉的,如果前一個數(shù)據(jù)的長度比后一個要長,那就會覆蓋不完,最好還有加一個清除函數(shù),這里介紹一種辦法使用運行庫函數(shù)memset():memset(str, 0, sizeof(str));這樣就可以把緩沖區(qū)的數(shù)據(jù)清除掉,當(dāng)然還有其他辦法,但是我就想到這個,可能不好用。但是我們這個項目里面覆蓋完不完并不會影響結(jié)果,所以也可以用,不過在需要把數(shù)據(jù)打印到串口這樣的項目中,就很有必要把之前數(shù)據(jù)給清除掉,不然容易出錯。
為了代碼的完整,下面我把整個主函數(shù)的代碼給貼出來,給各位伙伴參考。
- int main(void)
- {
-
- u16 t;
- u16 led0pwmval=0;
- u8 dir=1;
- delay_init(); //延時函數(shù)初始化
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置NVIC中斷分組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
- uart_init(115200); //串口初始化為115200
- LED_Init(); //LED端口初始化
- TIM3_PWM_Init(899,0); //不分頻。PWM頻率=72000000/900=80Khz
-
- while(1)
- {
- if(USART_RX_BUF[0]=='h'&&USART_RX_BUF[1]=='u'&&USART_RX_BUF[2]=='x'
- &&USART_RX_BUF[3]=='i')
- {
-
- t=1;
- USART_RX_STA=0;
- }
- else if(USART_RX_BUF[0]=='s'&&USART_RX_BUF[1]=='h'&&USART_RX_BUF[2]=='a'
- &&USART_RX_BUF[3]=='n'&&USART_RX_BUF[4]=='s'&&USART_RX_BUF[5]=='h'&&USART_RX_BUF[6]=='u'&&USART_RX_BUF[7]=='o')
- {
-
- t=0;
- USART_RX_STA=0;
-
- }
-
- if(t==1)
- {
- delay_ms(10);//去抖動
- if(dir)led0pwmval++;
- else led0pwmval--;
- if(led0pwmval>200)dir=0;
- if(led0pwmval==0)dir=1;
- TIM_SetCompare2(TIM3,led0pwmval);
-
- }
- if(t==0)
- {
- TIM_SetCompare2(TIM3,0);
- delay_ms(300);
- TIM_SetCompare2(TIM3,899);
- delay_ms(300);
-
- }
-
-
- }
- }
復(fù)制代碼
4、小結(jié)
1.在這個項目中要注意把PB5和PC13用杜邦線連到一樣哦,不然就看不到效果啦。
2.還有一個易錯點就是,在閃爍這個功能代碼中,很多人首先想到的肯定是讓那個GPIO口的電平置高或者置低來控制燈的閃爍,但是這樣子的話,你就不可以只用一個燈來實現(xiàn)呼吸和閃爍之間的轉(zhuǎn)換了,你需要使用兩個燈,一個呼吸一個閃爍,這樣子是比較麻煩的。但是也根據(jù)個人需要吧,如果想要只用一個燈實現(xiàn)兩個效果,就使用上面的方法,呼吸和閃爍都使用定時器3通道2來控制。這樣就可以達(dá)到轉(zhuǎn)換自如了。
3.就是數(shù)據(jù)覆蓋的問題,這個也是根據(jù)你要做的項目要解決吧,可以清除,也可以不用。
4.上面的辦法只是控制呼吸和閃爍的一種辦法,或許復(fù)雜了,希望有更加簡單辦法的大佬指導(dǎo)一下,我也是剛剛學(xué)習(xí),如有不懂的,可以私信交流,分享到此,謝謝.
|