基于STM32波形信號發(fā)生器proteus仿真設(shè)計(仿真+程序+報告+講解)
仿真圖proteus 8.9
程序編譯器:keil 5
編程語言:C語言
設(shè)計編號:C0075
講解仿真視頻:
主要功能:
結(jié)合實際情況,基于STM32F103單片機設(shè)計一個四種波形發(fā)生器(正弦波、方波、三角波、鋸齒波)。該系統(tǒng)應(yīng)滿足的功能要求為:
(1) 可以實現(xiàn)四種波形:正弦波、方波、三角波、鋸齒波;
(2) 通過按鍵進行選擇,頻率可以調(diào)整;
(3) LCD液晶顯示;
(4)設(shè)計出來之后用Proteus軟件仿真出效果;
主要硬件設(shè)備:STM32F103單片機、DAC0832數(shù)模轉(zhuǎn)換芯片、矩陣鍵盤、LCD12864液晶屏幕。
資料下載鏈接(可點擊):
【騰訊文檔】C0075 下載鏈接
以下為本設(shè)計資料展示圖:
整體設(shè)計方案
四種波形發(fā)生器以STM32F103單片機作為整個系統(tǒng)的控制核心,應(yīng)用其強大的處理速度,構(gòu)成波形發(fā)生器系統(tǒng)。該系統(tǒng)具備將數(shù)字信號轉(zhuǎn)換為模擬信號的能力。正弦波可以直接采用數(shù)學函數(shù)sin計算出來,送入單片機進行數(shù)據(jù)處理。經(jīng)單片機運算后的數(shù)據(jù)送入DAC0832芯片將數(shù)字信號轉(zhuǎn)換為模擬信號輸出。其他的波形都可以采用自身的規(guī)律采用不同的算法實現(xiàn)。
圖2-1 基于STM32單片機的四種波形發(fā)生器原理圖
本系統(tǒng)硬件主要由矩陣鍵盤、D/A轉(zhuǎn)換器、LCD12864顯示系統(tǒng)、處理器等幾部分組成。各模塊的主要功能如下:
(1)矩陣鍵盤的功能是設(shè)置波形和頻率,然后送入單片機。
(2) MCU的功能是識別鍵盤的數(shù)據(jù)并進行相對應(yīng)的處理,然后轉(zhuǎn)換出波形的數(shù)字信號和LCD顯示的數(shù)據(jù)。
(3) LCD12864顯示系統(tǒng)的功能是將設(shè)置的波形和頻率顯示出來。
系統(tǒng)的整體設(shè)計方案設(shè)計圖如圖2-2所示。
圖2-2 系統(tǒng)的整體方案設(shè)計圖
采用的是DAC0832芯片來做DA轉(zhuǎn)換的,DAC0832將輸出電壓分成了0xFF(255)份,需要輸出不同的波形我們需要給不同的數(shù)據(jù),在這里我將所有的波形的一個周期分成了100份,定時器每隔一段時間中斷一次,中斷100次為一個周期。
正弦波采用數(shù)學計算公式sin來計算;
方波只需要在定時器前面50次給0,后面50次給最大值即可;
三角波只需要在定時器前面50次采用最大值的50分之一乘于它本身,后面50次相反即可;
鋸齒波只需要在一個周期內(nèi),定時器中斷一次就用他本身乘于電壓最大值的100分之一即可;
測試波形如下所示:
三角波
鋸齒波
方波
正弦波
程序:
main函數(shù)
#define KEY0 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10) //讀取按鍵0
void Delay_Ms(u16 time);
/*************** 配置Switch用到的I/O口 *******************/
void Init_GPIO_Switch(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//關(guān)閉jtag,使能SWD,可以用SWD模式調(diào)試
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 使能PC端口時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PC0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //設(shè)置成輸入
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PC0
}
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
*功能名稱:main
*描述:主程序。
*輸入:無
*輸出:無
*返回:無
*******************************************************************************/
int main(void)
{
u8 i=0;
RCC_ClocksTypeDef RCC_Clocks; //初始化程序
RCC_Configuration(RCC_PLLMul_4); //8M*4 == 32M
RCC_GetClocksFreq(&RCC_Clocks); //獲取片上時鐘
Init_12864(); //初始化12864液晶
Key_Init();
Init_GPIO_Switch();
Init_GPIO_DAC0832();
Data0=25;
TIM3_Int_Init(50+Data0,320); //頻率:32000000/ 320 ==100 000 /100 == 1000 /50==20
LCD_P6x8Str(3,16," Sine Wave ");
LCD_P6x8Str(7,6*2,"Frequency: 15 Hz");
while (1)
{
if(KEY0)
{
if(i!=2)
{
__set_PRIMASK(1);
GPIO_ResetBits(GPIOB, ((uint16_t)0xC000));
}
Key_Test();
i=2;
}
else{
if(i!=5)
{
TIM3_Int_Init(50+Data0,320);
__set_PRIMASK(0); //使能TIMx外設(shè)
GPIO_ResetBits(GPIOB, ((uint16_t)0xC000));
}
i=5;
}
}
}
波形信號部分
/*************** 配置DAC用到的I/O口 *******************/
void Init_GPIO_DAC0832(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); // 使能PC端口時鐘
GPIO_InitStructure.GPIO_Pin = ((uint16_t)0x03FF); //選擇對應(yīng)的引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化PC端口
GPIO_SetBits(GPIOC, ((uint16_t)0x00FF)); // 高
GPIO_ResetBits(GPIOC, ((uint16_t)0x00FF)); // 低
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // 使能PC端口時鐘
GPIO_InitStructure.GPIO_Pin = ((uint16_t)0xC000); //選擇對應(yīng)的引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PC端口
GPIO_SetBits(GPIOC, ((uint16_t)0xC000)); // 高
GPIO_ResetBits(GPIOC, ((uint16_t)0xC000)); // 低
time=0;
ADC_CS_WR(0);
mode=0; //默認輸出正弦波
freq=100; //默認頻率
AM=255; //最大幅度
}
void DAC_0832_Data(uint8_t Data)
{
GPIO_ResetBits(GPIOC,~Data);
GPIO_SetBits(GPIOC,Data);
}
void sine_wave(u8 location)//輸出正弦波
{
double x=(double)location/50*PI;//把0-100放縮到0-2派(pai,沒有那個符號)
u8 y=(sin(x)*(AM/2)+(AM/2));//算出y,并放縮到0-254(因為ADC范圍0-AM,芯片落后)
DAC_0832_Data(y);
}
void squ_wave(u8 location)//方……
{
if(location<50)
DAC_0832_Data(AM);
else
DAC_0832_Data(0);//這個簡單
}
void tri_wave(u8 location)//三……
{
//為了簡化,在單周期輸出V字形
u8 y;
if(location<50)
y=(50-location)*AM/50;
else
y=(location-50)*AM/50;
DAC_0832_Data(y);
//偶函數(shù),當然說奇函數(shù)也沒錯
}
void saw_wave(u8 location)//鋸……
{
DAC_0832_Data(location*AM/100);
//用(100-location)也以變成反向鋸齒
}
//通用定時器中斷初始化
//這里時鐘選擇為APB1的2倍,而APB1為36M
//arr:自動重裝值。
//psc:時鐘預(yù)分頻數(shù)
//這里使用的是定時器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //時鐘使能
TIM_TimeBaseStructure.TIM_Period = arr; //設(shè)置在下一個更新事件裝入活動的自動重裝載寄存器周期的值 計數(shù)到5000為500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設(shè)置用來作為TIMx時鐘頻率除數(shù)的預(yù)分頻值 10Khz的計數(shù)頻率
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ù)單位
TIM_ITConfig( //使能或者失能指定的TIM中斷
TIM3, //TIM3
TIM_IT_Update ,
ENABLE //使能
);
// TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占優(yōu)先級0級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優(yōu)先級3級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外設(shè)
// TIM_Cmd(TIM3, DISABLE);
}
void TIM3_IRQHandler(void) //TIM3中斷
{
switch(mode)
{
case W_SINE:sine_wave((u8)(time*freq/100)%100);break;//計算出波的位置
case W_SQU:squ_wave((u8)((time*freq/100)%100));break;
case W_TRI:tri_wave((u8)((time*freq/100)%100));break;
case W_SAW:saw_wave((u8)((time*freq/100)%100));break;
}
time++;
if(time>=100)//計數(shù)100次
time=0;
}
按鍵識別
/************************************
按鍵表盤為: 1 2 3 A
4 5 6 B
7 8 9 C
* 0 # D
************************************/
u8 ee,Data0=0;
void Key_Test(void)
{
int num;
char Freq[3]={'?'};
num = Key_Scan();
if(ee!=num)
switch(num)
{
case 0: LCD_P6x8Str(1,1,"0"); break;
case 1: LCD_P6x8Str(1,1,"1"); break;
case 2: LCD_P6x8Str(1,1,"2"); break;
case 3: LCD_P6x8Str(1,1,"3"); break;
case 4: LCD_P6x8Str(1,1,"4"); break;
case 5: LCD_P6x8Str(1,1,"5"); break;
case 6: LCD_P6x8Str(1,1,"6"); break;
case 7: LCD_P6x8Str(1,1,"7"); break;
case 8: LCD_P6x8Str(1,1,"8"); break;
case 9: LCD_P6x8Str(1,1,"9"); break;//
case 'A': LCD_P6x8Str(1,1,"A"); mode=0;LCD_P6x8Str(3,16," Sine Wave "); break;
case 'B': LCD_P6x8Str(1,1,"B"); mode=1;LCD_P6x8Str(3,16," Square wave "); break;
case 'C': LCD_P6x8Str(1,1,"C"); mode=2;LCD_P6x8Str(3,16,"Ttiangular wave"); break;
case 'D': LCD_P6x8Str(1,1,"D"); mode=3;LCD_P6x8Str(3,16," Sawtooth Wave "); break;
case '#': LCD_P6x8Str(1,1,"#"); if(Data0>=5)Data0-=5; Freq[0]=(20-Data0/5)/10+'0';Freq[1]=(20-Data0/5)%10+'0'; LCD_P6x8Str(7,6*13,Freq); break;
case '*': LCD_P6x8Str(1,1,"*"); if(Data0<50)Data0+=5; Freq[0]=(20-Data0/5)/10+'0';Freq[1]=(20-Data0/5)%10+'0'; LCD_P6x8Str(7,6*13,Freq); break;
}
ee=num;
}
資料清單:
下載鏈接見文章最開頭