|
|
1. 程序里面想詳細(xì)的算法 思路,
2. 在晶振和CPU滿足計(jì)算的情況下,理論是你要多少你就通過(guò)編碼器調(diào)節(jié)多少
3. 本程序任意頻率調(diào)試是通過(guò)編碼器來(lái)切換的,
4.關(guān)于精度問(wèn)題 在100hz內(nèi)非常準(zhǔn),在最大255HZ的時(shí)候相差20hz左右, 問(wèn)題可能出現(xiàn)在 定時(shí)器計(jì)數(shù)這個(gè)位置, 我全部取整數(shù)了 所以誤差比較大
如果計(jì)數(shù)的出來(lái)本來(lái)機(jī)是整數(shù)的,那頻率相對(duì)準(zhǔn)確的, 在100hz的時(shí)候就是標(biāo)準(zhǔn)的100hz
注意看頻率
單片機(jī)源程序如下:
/*------------------------------------------------------------------*/
/* --- 功能 pwm輸出 頻率可調(diào) -------------------------- -----------*/
/* --- STC12C5Axx -------*/
/* --- 對(duì)于定時(shí)器選擇的時(shí)候,應(yīng)該選擇16位---------------------------*/
/* --- 不應(yīng)該選擇16位自動(dòng)重裝,因?yàn)?6位自動(dòng)重的工作原理是當(dāng)TL0------*/
/* --- 溢出的時(shí)候,會(huì)直接把TH0的值填充到TL0里面計(jì)算這樣就會(huì)導(dǎo)致-----*/
/* --- PWM計(jì)算頻率出錯(cuò) -----------------------------------------*/
/* --- 定時(shí)的工作方式為。定時(shí)器0的工作模式為16位--------------------*/
/* 計(jì)算方法 例如12MHZ需要轉(zhuǎn)化為300HZ,那么根據(jù)上圖,首先我們需要確定
PCA時(shí)鐘輸入頻率,根據(jù)公式 300*256=76800HZ,這個(gè)值就是我們需要的PCA時(shí)
鐘輸入頻率。現(xiàn)在問(wèn)題就是 ,我們?cè)趺窗?2MHZ,轉(zhuǎn)化為76.8KHZ,
12000KHZ/76.8KHZ=156.25 ,這個(gè)156.25就是分頻基數(shù),而這個(gè)分頻基數(shù)由
我們的定時(shí)器溢出參數(shù)來(lái)設(shè)定,意思就是當(dāng)我們定時(shí)器如果計(jì)數(shù)156.25溢出就
可以做到分頻基數(shù)為156.25, 所以我們?cè)谠O(shè)置定時(shí)器0的計(jì)數(shù)起始值就
是65536-156=65380,對(duì)應(yīng)TH0=0XFF,TL0=0X64。 0XFF=255 0X64=100
初始是如何分離的計(jì)算方法 65380/256=255 ps:取的整數(shù),然后用255*256=65280
再用65380-65280=100這個(gè)就是 65380對(duì)256求于的個(gè)數(shù)放在TL0里面也就是0X64
那么TH0就應(yīng)該放255將255轉(zhuǎn)化為16進(jìn)制數(shù)為0xff */
/*整體計(jì)算公式(65536-12000/300*256)/256
/* article, please specify in which data and procedures from STC */
/*------------------------------------------------------------------*/
/*按鍵k1 k2 分別接在 P0^6 P^7口 用的是獨(dú)立鍵盤*/
/*實(shí)現(xiàn)的功能是,控制CR的開(kāi)通和關(guān)斷來(lái)實(shí)現(xiàn)混頻效果*/
/*第二次修改時(shí)間2019-10-6*/
/*作者 Alan*/
#include <STC12C5A60S2.H>
#include <stdint.h>
//#include <intrins.h>
#define FOSC 18432000
/*分別定義了兩個(gè)不高低位的變量用于保存 定時(shí)器初值,從而初值計(jì)算里面帶變量計(jì)算以后保存到這個(gè)變量 這時(shí)候就能在中斷正常重新裝初值了,也不會(huì)出錯(cuò)*/
uint8_t TH,TL;
/*上一次的狀態(tài)*/
uint8_t Last_Amb_Status = 0;
/*該變量用于保存按鍵值*/
uint8_t Sd_Key_Value = 0;
uint8_t Sd_Key_Value2 = 0;
/*編碼器三個(gè)I/O的定義 分別為,A口 B口 sdKey */
sbit Pin_Portry_A=P2^4 ;
sbit Pin_Portry_B=P2^3 ;
sbit Pin_Portry_Sd=P2^2 ;
/*testing 測(cè)試端口 暫時(shí)不用*/
sbit BUZ = P3^0;
sbit LED = P3^7;
sbit k1 = P0^6;
sbit k2 = P0^7;
sbit LED_1 = P0^5;
unsigned char flag=0;
uint8_t Data_key2(uint8_t *key_val2);
/*延時(shí)1ms級(jí)函數(shù)2*/
void delay(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=135;y>0;y--);
}
/*副頻自動(dòng)變化*/ //有缺陷 放棄 重新構(gòu)思 但是這里取消了 不影響主頻哈
void fuping_bianhua(uint8_t XHZ)
{
uint8_t i;
for(i=0;i<XHZ;i++)
{
CR = 1;
delay(25);
CR = 0;
delay(25);
}
}
/*旋轉(zhuǎn)編碼器調(diào)整副頻*/
uint8_t Data_key2(uint8_t *key_val2) //*key_val這個(gè)是一個(gè)指針嗎
{
//這個(gè)counter是為了防止沒(méi)有鍵值動(dòng)作時(shí),程序一直在這里等待
//具體的等待時(shí)間應(yīng)該根據(jù)編碼開(kāi)關(guān)的脈沖周期和程序的執(zhí)行周期來(lái)確定
//可以根據(jù)實(shí)際情況進(jìn)行調(diào)整,連續(xù)測(cè)試的時(shí)候,可以先去掉此限定條件
uint16_t wait_counter = 100000; //counter等待的時(shí)間
uint8_t temp_key_val2 = *key_val2;
//temp_key_val 這個(gè)是char型的變量 最大255 所以不需要設(shè)置最大值
Last_Amb_Status = Pin_Portry_A; //保存采樣前的A口狀態(tài)
//開(kāi)始采樣端口A的跳變邊沿,如果沒(méi)有產(chǎn)生跳變
//A口的當(dāng)前采樣值和之前保存的值是一樣的,異或后值為0
//while持續(xù)等待兩個(gè)值不同時(shí),跳過(guò)while執(zhí)行下一步驟,如果等待到wait counter為0 的時(shí)候就跳出,然后執(zhí)行下一步;
while( !(Pin_Portry_A ^ Last_Amb_Status) && --wait_counter);
if(!wait_counter) //在while語(yǔ)句期間 如果A口發(fā)生變化 除了跳出 while 同時(shí)也跳過(guò)這里 這里不能打冒號(hào)
return 0; //跳過(guò)這里
//此時(shí)采樣B口的電平
//如果B口的值和采樣A口跳變沿之前的值相同,判斷為順時(shí)針旋轉(zhuǎn)
//如果B口的值和采樣A口跳變沿之前的值不同,判斷為逆時(shí)針旋轉(zhuǎn)
if(!(Pin_Portry_B ^ Last_Amb_Status))
{
//順時(shí)針旋轉(zhuǎn)
temp_key_val2++;
if(temp_key_val2 < 2)
temp_key_val2 = 2;
if(temp_key_val2 > 40); //如果大于40就截止 限制最大
temp_key_val2 = 2; //重新設(shè)置為2
*key_val2 = temp_key_val2;
return 1;
}
else if(Pin_Portry_B ^ Last_Amb_Status) //和上面正轉(zhuǎn)工作過(guò)程一樣 zc注釋
{
//逆時(shí)針旋轉(zhuǎn)
temp_key_val2--;
if(temp_key_val2 > 40) //如果逆時(shí)針大于40就截止
temp_key_val2 = 2; //重新設(shè)置為2
if(temp_key_val2 <= 2) //如果小于2 就設(shè)置為最大40
temp_key_val2 = 40;
*key_val2 = temp_key_val2;
return 1;
}
return 0;
}
/* 定時(shí)器配置與初始化*/
void Timer0Init(void)
{
/*定義一個(gè)32位整形變量 temp 存放編碼器初值*/
uint32_t temp = 5;
/*設(shè)置定時(shí)器0位1T模式*/
/*先將tmod高4位保存為1 “TMOD=$=0XF0”,這樣是防止多個(gè)定時(shí)器使用的時(shí)候,直接賦值導(dǎo)致其它定時(shí)器工作異常,或者是各定時(shí)器無(wú)法突出自己的作用*/
/*通過(guò)上面保存了高4位后,接下來(lái)進(jìn)行或運(yùn)算"TMOD=|=0X01",從而只是將我們需要的位打開(kāi)其它全部關(guān)閉,這樣非常穩(wěn)妥,互補(bǔ)相干*/
AUXR |= 0x80;
TMOD &= 0xF0;
TMOD |= 0x01;
/*把要記的次數(shù),通過(guò)下面的運(yùn)算,算出來(lái)以后,保存到temp里面,供定時(shí)器裝初值用*/
temp = 0x10000-(18432000/(temp*256));
/*temp內(nèi)部保存的次數(shù),分別裝在定時(shí)器的高8位和低8位*/
TH0 = TH = temp / 256;
TL0 = TL = temp % 256; //設(shè)置定時(shí)器初值
/*啟動(dòng)定時(shí)器相關(guān)功能,清楚標(biāo)志位,啟動(dòng)定時(shí)器TR0,開(kāi)總中斷,開(kāi)定時(shí)器中斷ET0*/
TF0 = 0; //清除TF0標(biāo)志
TR0 = 1; //定時(shí)器0開(kāi)始計(jì)時(shí)
EA=1;
ET0=1;
}
/*對(duì)key函數(shù)聲明*/
uint8_t Data_key(uint8_t *key_val);
/*對(duì)頻率更新函數(shù)聲明*/
void pinglvgengxing(uint8_t key_val);
void main(void)
{
Timer0Init(); //初始化定時(shí)器配置函數(shù)
CCON = 0;
CL = 0;
CH = 0;
/*將PCA fosc模式設(shè)置為 定時(shí)器0溢出率*/
CMOD = 0x04;
/*setting CCAP0H 脈寬為50%輸出*/
CCAP0H = CCAP0L = 0x80;
/*setting PCA模式為8bit自動(dòng)重裝模式*/
CCAPM0 = 0x42;
/*setting CCAP1H 脈寬為50%輸出*/
CCAP1H = CCAP1L = 0x80;
PCA_PWM1 = 0x00;
/*setting PCA模式為8bit自動(dòng)重裝模式*/
CCAPM1 = 0x42;
/*PCA時(shí)鐘開(kāi)始運(yùn)行,計(jì)數(shù), 輸出pwm信號(hào)*/
CR = 1;
while (1)
{
if(0 == flag)
{
LED=0;
if(Data_key(&Sd_Key_Value));
else if(Pin_Portry_Sd!=1) // 判斷按鍵是否被按下
{
delay(5); //消抖動(dòng)
if(Pin_Portry_Sd!=1) //再次判斷
while(!Pin_Portry_Sd);
LED=1;
flag=1;
}
}
else if(1 == flag)
{
LED_1=0;
if(Data_key2(&Sd_Key_Value2));
if(Pin_Portry_Sd!=1) //再次判斷
{
delay(5);
while(!Pin_Portry_Sd);
LED_1=1;
flag=0;
}
}
fuping_bianhua(Sd_Key_Value2);
// 如果要在1602上顯示對(duì)應(yīng)的頻率,直接將“Sd_Key_Value2”變化寫數(shù)據(jù)到1602
//記住要分離式數(shù)據(jù)哦 這個(gè)設(shè)計(jì)是0-255HZ 誤差有點(diǎn)大 開(kāi)到255的時(shí)候頻率誤差在20-30hz
//這個(gè)誤差主要是來(lái)自PCA時(shí)鐘運(yùn)算這里,主要是計(jì)算的時(shí)候出現(xiàn)了小數(shù)點(diǎn)就省略了 如果從新
//將小數(shù)點(diǎn)保留 頻率是很準(zhǔn)的 在100hz內(nèi)的頻率基本就很準(zhǔn)的 自己去調(diào)整吧
pinglvgengxing(Sd_Key_Value);
}
}
/*頻率更新函數(shù)*/
void pinglvgengxing(uint8_t key_val)
{
uint32_t temp;
//關(guān)閉定時(shí)器中斷
//關(guān)閉全局中斷
//失能定時(shí)器
//清除定時(shí)器溢出標(biāo)志位
ET0 = 0;
EA = 0;
TR0 = 0;
TF0 = 0;
//重新初始化定時(shí)初值
temp = 0x10000-18432000/(key_val*256) ;
TH0 =TH= temp/256; //設(shè)置定時(shí)初值
TL0 =TL= temp%256; //設(shè)置定時(shí)初值
//開(kāi)啟全局中斷
//開(kāi)啟定時(shí)器中斷
//使能定時(shí)器
EA = 1;
ET0 = 1;
TR0 = 1;
}
/*主頻按鍵函數(shù)*/
uint8_t Data_key(uint8_t *key_val) //*key_val這個(gè)是一個(gè)指針嗎
{
//這個(gè)counter是為了防止沒(méi)有鍵值動(dòng)作時(shí),程序一直在這里等待
//具體的等待時(shí)間應(yīng)該根據(jù)編碼開(kāi)關(guān)的脈沖周期和程序的執(zhí)行周期來(lái)確定
//可以根據(jù)實(shí)際情況進(jìn)行調(diào)整,連續(xù)測(cè)試的時(shí)候,可以先去掉此限定條件
uint16_t wait_counter = 100000; //counter等待的時(shí)間
uint8_t temp_key_val = *key_val; //這個(gè)是一個(gè)指針變量嗎? 如果是 那它是指向 (Sd_Key_Value) 這里面的嗎 //我還沒(méi)有學(xué)過(guò)指針哈哈哈
/*把A口的值保存到當(dāng)前狀態(tài) 變量里面*/
Last_Amb_Status = Pin_Portry_A; //保存采樣前的A口狀態(tài)
//開(kāi)始采樣端口A的跳變邊沿,如果沒(méi)有產(chǎn)生跳變
//A口的當(dāng)前采樣值和之前保存的值是一樣的,異或后值為0
//while持續(xù)等待兩個(gè)值不同時(shí),跳過(guò)while執(zhí)行下一步驟,如果等待到wait counter為0 的時(shí)候就跳出,然后執(zhí)行下一步;
while( !(Pin_Portry_A ^ Last_Amb_Status) && --wait_counter);
if(!wait_counter) //在while語(yǔ)句期間 如果A口發(fā)生變化 除了跳出 while 同時(shí)也跳過(guò)這里 這里不能打冒號(hào)
return 0; //跳過(guò)這里
//此時(shí)采樣B口的電平
//如果B口的值和采樣A口跳變沿之前的值相同,判斷為順時(shí)針旋轉(zhuǎn)
//如果B口的值和采樣A口跳變沿之前的值不同,判斷為逆時(shí)針旋轉(zhuǎn)
if(!(Pin_Portry_B ^ Last_Amb_Status))
{
//順時(shí)針旋轉(zhuǎn)
temp_key_val++;
if(temp_key_val < 5)
temp_key_val = 5;
//if(temp_key_val > 40) //如果大于40就截止
// temp_key_val = 2; //重新設(shè)置為2
//我感覺(jué)這個(gè)是一個(gè)指針變量 將按鍵值傳遞給指針 然后指針指向Sd_Key_Value地址 然后就可以將 temp_key_val;結(jié)果傳遞給Sd_Key_Value
*key_val = temp_key_val;
return 1;
}
else if(Pin_Portry_B ^ Last_Amb_Status) //和上面正轉(zhuǎn)工作過(guò)程一樣 zc注釋
{
//逆時(shí)針旋轉(zhuǎn)
temp_key_val--;
// if(temp_key_val2 > 40) //如果逆時(shí)針大于40就截止
// temp_key_val2 = 2; //重新設(shè)置為2
if(temp_key_val <= 5)
temp_key_val =255; //限制最小
*key_val = temp_key_val;
return 1;
}
return 0;
}
/*定時(shí)器0中斷*/
void tm0_isr(void) interrupt 1 using 1
{
TH0 =TH; //設(shè)置定時(shí)初值
TL0 =TL; //設(shè)置定時(shí)初值
}
以上程序51hei下載地址:
通過(guò)編碼器開(kāi)關(guān)來(lái)控制 混頻程序V1.0.rar
(37.77 KB, 下載次數(shù): 18)
2019-11-11 20:06 上傳
點(diǎn)擊文件名下載附件
|
評(píng)分
-
查看全部評(píng)分
|