<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    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調其他音對應頻率見文末注腳1
    • 每個音都是要持續一定時間的,以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/m_24325[i*2]);
    			GPIO_WriteBit(BeeGpio,Bee,(BitAction)(1)); 
    			delay_us(500000/m_24325[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%)預計下周會發布,歡迎感興趣的小伙伴與我共同學習,一起進步!


    C調低音頻率(Hz)C調中音頻率(Hz)C調高音頻率(Hz)
    1262152311046
    1#2771#5541#1109
    2294258721175
    2#3112#6222#1245
    3330365931318
    4349469841397
    4#3704#7404#1480
    5392578451568
    5#4155#8315#1661
    6440688061760
    6#4666#9326#1865
    7494798871976

    1. ??
    ??2020 CSDN 皮膚主題: 黑客帝國 設計師:上身試試 返回首頁
    多乐彩