520了,用32做個簡單的小程序

520到了,看着朋友圈裏的花式秀恩愛,平常午餐最愛吃的泡麪都變得不那麼香了。於是!突發奇想,突然就來了更新的想法,今天用32來做一個非常簡單的小程序:

stm32f103c8t6

因爲基本只用到兩個外設,程序容量也很小,所以用c8t6就剛剛好
在這裏插入圖片描述

無源蜂鳴器

這裏要用的是無源蜂鳴器,其音調是可調的。

庫函數

我們先聲明要用到的引腳以及相應的函數:

#define BeeGpio	GPIO自選
#define Bee 	GPIO_Pin_自選	

void Bee_Init(void); //蜂鳴器初始化
void Bee_test(void); //蜂鳴器測試
void Play_Music(void);//播放音樂

void Bee_Init(void)

這個也非常好理解,和初始化引腳是一樣的 。

void Bee_Init(void){
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    GPIO_InitStructure.GPIO_Pin = Bee; 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推輓輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(BeeGpio, &GPIO_InitStructure);	
	
	GPIO_WriteBit(BeeGpio,Bee,(BitAction)(1)); 
}

void Bee_test(void)

在主函數演奏之前,我們先測試一下蜂鳴器好不好使,讓它先響一聲:

void Bee_test(void){ 
	u16 i;
	for(i=0;i<200;i++){
		GPIO_WriteBit(BeeGpio,Bee,(BitAction)(0));
		delay_us(500); 
		GPIO_WriteBit(BeeGpio,Bee,(BitAction)(1));
		delay_us(500);
	}
}

爲了給下文的演奏做鋪墊,發出聲響的原理現在要着重強調一下:
(delay函數是已經寫好的,有us、ms、s等等單位,這裏用的是us)

  • 在這個for循環裏,先後兩次的delay_us(500)加在一起構成了一個週期,這個週期的時間長是1000us,也就是1ms。在這1ms的時間裏,一半的時間蜂鳴器不響,另一半的時間響,如此重複200次,就成爲了我們人類耳朵聽到的一個時間約爲200ms的響聲。

樂譜(簡譜)

以一個非常簡單的粉刷匠爲例:(希望我沒有記錯譜子哈哈哈)
在這裏插入圖片描述

  • C調中音12345對應的聲音頻率分別是:523、587、659、698、784Hz。所以我們就可以把簡譜中的數字依次替換(C調其他音對應頻率見文末)
  • 每個音都是要持續一定時間的,以ms爲單位,比如“2432”的聲音要保持一致,而“5-”要持續略長的時間

以“2432|5-”爲例,我們把音調與對應的時間 兩兩一組,放到一個數組裏:

uc16 m_24325[10]={//奇數項爲頻率,偶數項爲持續時間(ms)
	587,300,
	698,300,
	659,300,
	587,300,
	784,750,
};

我在測試的時候發現如果嚴格按照音調對應頻率的話,聽起來反而與想象中的音樂差了不少(難道是蜂鳴器的事?)所以稍微改了一下頻率。

void Play_Music(void)

 void Play_Music(void){ 
	u16 i,j;
	for(i=0;i<5;i++){
		for(j=0;j<m_24325[i*2]*m_24325[i*2+1]/1000;j++){
			GPIO_WriteBit(BeeGpio,Bee,(BitAction)(0));
			delay_us(500000/music1[i*2]);
			GPIO_WriteBit(BeeGpio,Bee,(BitAction)(1)); 
			delay_us(500000/music1[i*2]); 
		}	
	}
}
  • 因爲在前文的樂譜中,記了10個數據,5對音調與時間,所以令i=0;i<5
  • 在第二個for循環中,先後兩次delay_us(500000/music1[i*2]),使得週期變爲1000 000/頻率
  • 而 j<m_24325[i*2]m_24325[i2+1]/1000 和 週期共同決定了蜂鳴器發出這個頻率對應音調的時間

演算一下:以“523Hz”響750ms爲例:
在這裏插入圖片描述
如此,我們便能演奏一些基本的曲子了,只需要自己寫一個樂譜就好了。
void Play_Music(void)也可以寫爲有輸入參數的函數,這樣便於我們用同一個函數調用不同的樂譜。

接下來就到了另一個模塊:

OLED模塊(7腳64*128)

買到OLED模塊以後,商家往往都會附贈配套程序的,不過往往都會贈IIC的程序。這裏把我以前用的SPI程序放上。

模擬SPI

.h

#define OLED_CMD 0   
#define OLED_DATA 1 

#define OLED_CLK    PAout(4)  
#define OLED_MOSI   PAout(3)   
#define OLED_RST    PAout(2)   
#define OLED_DC     PAout(1)  

void OLED_SPI_Init(void); 
void SPI_WriteByte(uint8_t addr,uint8_t data); 
void WriteCmd(unsigned char cmd); 
void WriteData(unsigned char data); 

.c

void OLED_SPI_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; 
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
}


void SPI_WriteByte(unsigned char data,unsigned char cmd)
{
    unsigned char i=0;
    OLED_DC =cmd;
    OLED_CLK=0;
    for(i=0;i<8;i++)
    {
        OLED_CLK=0;
        if(data&0x80)OLED_MOSI=1; 
        else OLED_MOSI=0;
        OLED_CLK=1;
        data<<=1;
    }
    OLED_CLK=1;
    OLED_DC=1;
}

void WriteCmd(unsigned char cmd)
{
    SPI_WriteByte(cmd,OLED_CMD);
}

void WriteData(unsigned char data)
{
    SPI_WriteByte(data,OLED_DATA);
}

OLED

.h

void OLED_Init(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_Refresh_Gram(void);
void OLED_Clear(void);

.c

u8 OLED_GRAM[128][8];   

void OLED_DLY_ms(unsigned int ms)
{                         
  unsigned int a;
  while(ms)
  {
    a=1335;
    while(a--);
    ms--;
  }
}

void OLED_Init(void)
{
    OLED_SPI_Init();
    OLED_CLK = 1;
    OLED_RST = 0;
    OLED_DLY_ms(100);
    OLED_RST = 1;

      WriteCmd(0xae);
	  WriteCmd(0x00);
	  WriteCmd(0x10);
	  WriteCmd(0xd5);
	  WriteCmd(0x80);
	  WriteCmd(0xa8);
	  WriteCmd(0x3f);
	  WriteCmd(0xd3);
	  WriteCmd(0x00);
	  WriteCmd(0xB0);
	  WriteCmd(0x40);
	  WriteCmd(0x8d);
	  WriteCmd(0x14);
	  WriteCmd(0xa1);
	  WriteCmd(0xc8);
	  WriteCmd(0xda);
	  WriteCmd(0x12);
	  WriteCmd(0x81);
	  WriteCmd(0xff);
	  WriteCmd(0xd9);
	  WriteCmd(0xf1);
	  WriteCmd(0xdb);
	  WriteCmd(0x30);
	  WriteCmd(0x20);
	  WriteCmd(0x00);
	  WriteCmd(0xa4);
	  WriteCmd(0xa6);
	  WriteCmd(0xaf); 

    OLED_Clear(); 
}

void OLED_Refresh_Gram(void)
{
    u8 i,n;         
    for(i=0;i<8;i++)  
    {  
        WriteCmd(0xb0+i);   
        WriteCmd(0x00);      
        WriteCmd(0x10);      
        for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]); 
    }   
}

void OLED_Clear(void)  
{  
   	u8 j,t;
	for(t=0xB0;t<0xB8;t++){
	   WriteCmd(t);
		 WriteCmd(0x10);
		 WriteCmd(0x00);	
		for(j=0;j<132;j++){
 			    WriteData(0x11);
 		}
	}
}

顯示16*16的字符

這個是仿照商家的IIC例程改成SPI的寫法,其實驅動OLED的方法都是一樣的,只不過IIC和SPI略有不同而已(3個輸入參數會在稍後講到)

void OLED_DISPLAY_16x16(u8 x,u8 y, u16 w){ 
	u8 j,t,c=0;
	y=y-14;
	for(t=0;t<2;t++){
		WriteCmd(0xb0+x); 
		WriteCmd(y/16+0x10); 
		WriteCmd(y%16);
		for(j=0;j<16;j++){
 			WriteData(M_16[(w*32)+c]);
			c++;}x++;
	}
	WriteCmd(0xAF); 
}
  • 第一個參數x:字符的行:0、2、4、6共4行(4*16=64,把64個像素分爲4行)
  • 第二個參數y:字符的列:共128列(像素),但是因爲字符是16*16的,所以用n * 16代替,便於計算
  • 第三個參數w:對應庫中的第幾個字符
  • 庫:M_16(在倒數第5行),這個內容馬上就講到

比如:OLED_DISPLAY_16x16(4,8*16,8),在OLED屏幕第3行的第8列,顯示中的第9個字符

字符/圖片庫,取模

這個庫是需要咱們自己建立的,可以由取模軟件自動生成每個字符對應的16進制數據。
我們用到的取模軟件是:PCtoLCD2002
配置如圖:
在這裏插入圖片描述
用它生成數據以後就可以把數據放到一個單獨的.h文件中,作爲我們自己的字符庫。這裏以兩個16*16的空白爲例

uc8 M_16[] = {
  //" "
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  //" "
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

};

這個軟件還是很好用的,而且像素也可以自定,比如用64 * 128的圖片鋪滿oled等等。詳見生日快樂(b站投稿)。這裏用的就是杜洋老師的開發板,我最初學32的時候就是學習杜洋老師的教程,雖然後來我又學了野火的32,正點原子的linux…(我很專一的/doge)

跑題了,這裏只是舉了一個16 * 16字符的例子,還有8 * 16字符、字符串、64 * 128圖片等等,就請各位自己研究了/doge


我是康,希望做一名能幫助到各位的博主! 我不是本來要更機器學習的嘛? 在做了在做了(0%)預計下週會發布,歡迎感興趣的小夥伴與我共同學習,一起進步!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章