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