13120|27

855

帖子

5

TA的资源

一粒金砂(高级)

楼主
 

【直播+开源毕设】基于STM32F4的网络收音机设计与实现 [复制链接]

 
        自从考研复试结束以后,就开始了俺的毕业设计了。这个题目说实话,想做出来简单,想做好比较难。好了,言归正传,开始说说设计思路吧。

      硬件方案如下:
1.主控制器采用ST公司的STM32F407ZGT6,该芯片基于Cortex M4内核,带有FPU浮点运算单元,和DSP指令集。主频168MHZ,拥有192KB片内SRAM和1MB的Flash空间
2.显示部分采用单色12864液晶,采用此种方案主要在于网络收音机并不需要显示彩色图片,仅仅显示状态即可。综合考虑,此液晶性价比较高。
3.解码部分:采用了欧胜公司(Wolfson)的WM8978。作为一个音频输出设备,音质是第一要考虑的, WM8978是Wolfson新近推出的一款全功能音频处理器。它带有一个HI-FI级数字信号处理内核,支持增强3D硬件环绕音效,以及5频段的硬件均衡器,可以有效改善音质;并有一个可编程的陷波滤波器,用以去除屏幕开、切换等噪音。WM8978同样集成了对麦克风的支持,以及用于一个强悍的扬声器功放,可提供高达900mW的高质量音响效果扬声器功率。一个数字回放限制器可防止扬声器声音过载。WM8978进一步提升了耳机放大器输出功率,在推动16欧姆耳机的时候,每声道最大输出功率高达40毫瓦。
4.网络PHY芯片:采用LAN8720,由于STM32F407ZGT6本身自带MAC层,因此实现网络通信只需要加一片PHY芯片即可。PHY芯片通过RMII接口和主控MAC通信,主控通过SMI接口配置PHY芯片。
5.字库及存储:SD卡,FLASH芯片,EEPROM。
软件方案如下:
1.操作系统选用UCOSII,实时性好。
2.操作界面移植STemwin负责某些复杂人机界面实现。
3.文件系统采用znFAT,用来读取SD卡和Flash芯片中的数据。
4.音频解码库采用Libmad开源软件库。(libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。
5.网络协议栈采用LwIP。LwIP是LightWeight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。
PS:本人所用的硬件平台是正点原子出的探索者M4开发板。一方面由于这块板上的硬件刚好够做这个题目,另一方面是手头资金实在不充裕。因此也就暂时没有画自己的硬件板。
此帖出自stm32/stm8论坛

最新回复

楼主你好,我刚才下载了你驱动程序,但是有错误显示..\CORE\core_cm4.h(169): error:  #5: cannot open source input file "core_cmInstr.h": No such file or directory。   详情 回复 发表于 2019-7-24 10:53

赞赏

1

查看全部赞赏

点赞 关注(1)
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 

回复
举报

855

帖子

5

TA的资源

一粒金砂(高级)

沙发
 

驱动LCD12864画点,显示字符和汉字

本帖最后由 人民币的幻想 于 2015-4-16 09:17 编辑

今天的进度是把12864液晶显示字符,汉字,画点函数搞定了。
常规驱动12864有以下几种实现:
1.      IO模拟80并口。
2.      FSMC总线驱动
3.      IO模拟串行驱动
4.      硬件SPI驱动
暂时选用的IO模拟80并口方式,这种办法较为简单,易于实现。下面详述此方案:
受限于板子的IO分配,液晶和MCU引脚连接如下:
/*************************************************************************************
LCD12864
LCD_RS     PB1
LCD_RW     PE3
LCD_EN     PE4
LCD_PSB    PE5
LCD_RST    接到板子RST上
LCD_D0     PE6
LCD_D1     PF6
LCD_D2     PF11
LCD_D3     PG6
LCD_D4     PG7
LCD_D5     PG8
LCD_D6     PG12
LCD_D7     PG15
LCD_BL     PB0
************************************************************************************/
由于分配了不连续的IO,因此读写数据函数的实现比往常困难一点,不过只要熟悉C语言的位运算,这都不叫事~~。
//数据格式
//D7 D6 D5 D4 D3 D2 D1 D0
uint8_t ReadByteFromLCD(void)
{
   uint8_t res=0;
         res=(LCD_D0_IN<<0)|(LCD_D1_IN<<1)
            |(LCD_D2_IN<<2)|(LCD_D3_IN<<3)
            |(LCD_D4_IN<<4)|(LCD_D5_IN<<5)
            |(LCD_D6_IN<<6)|(LCD_D7_IN<<7);
         returnres;
}
void WriteByteToLCD(uint8_t byte)
{
    LCD_D0_OUT=(byte&0x01)>>0;
          LCD_D1_OUT=(byte&0x02)>>1;
          LCD_D2_OUT=(byte&0x04)>>2;
          LCD_D3_OUT=(byte&0x08)>>3;
          LCD_D4_OUT=(byte&0x10)>>4;
          LCD_D5_OUT=(byte&0x20)>>5;
          LCD_D6_OUT=(byte&0x40)>>6;
          LCD_D7_OUT=(byte&0x80)>>7;
}
以上就是读写函数的实现,不过不要忘了在读数据之前将IO口设为输入,写数据之前将IO口设为输出。
/*************************************************************************************
LCD判忙函数
1:忙
0:不忙
************************************************************************************/
uint8_t LCD_Busy(void)
{
uint8_t  res=0;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
  //数据线IO方向设定
GPIO_Set(GPIOE,PIN6,GPIO_MODE_IN,0,0,GPIO_PUPD_PU);
GPIO_Set(GPIOF,PIN6|PIN11,GPIO_MODE_IN,0,0,GPIO_PUPD_PU);
GPIO_Set(GPIOG,PIN6|PIN7|PIN8|PIN12|PIN15,GPIO_MODE_IN,0,0,GPIO_PUPD_PU);
delay_us(5);
  //读数据
res=(ReadByteFromLCD()&0x80);
LCD_EN = 0;         
  
return res;
}
12864液晶和高速MCU通信的时候需要注意检测忙信号,以免导致错误的发生。接下来就需要实现写数据/指令到LCD的函数了。
//写指令/数据到LCD
void WriteToLCD(uint8_t mode,uint8_t byte)
{
   
         if(mode==LCD_CMD)//写指令
         {
           while(LCD_Busy());
           LCD_RS = 0;//指令
     LCD_RW = 0;//写
     LCD_EN = 0;
           delay_us(5);
     GPIO_Set(GPIOE,PIN6,GPIO_MODE_OUT,GPIO_OTYPE_OD,GPIO_SPEED_100M,GPIO_PUPD_PU);
     GPIO_Set(GPIOF,PIN6|PIN11,GPIO_MODE_OUT,GPIO_OTYPE_OD,GPIO_SPEED_100M,GPIO_PUPD_PU);
     GPIO_Set(GPIOG,PIN6|PIN7|PIN8|PIN12|PIN15,GPIO_MODE_OUT,GPIO_OTYPE_OD,GPIO_SPEED_100M,GPIO_PUPD_PU);
           WriteByteToLCD(byte);//写
           delay_us(5);
           
           LCD_EN = 1;
           delay_us(5);
           LCD_EN = 0;
                  
         }else            //写数据
         {
            while(LCD_Busy());
            LCD_RS = 1;//数据
            LCD_RW = 0;
            LCD_EN = 0;
            delay_us(5);
      GPIO_Set(GPIOE,PIN6,GPIO_MODE_OUT,GPIO_OTYPE_OD,GPIO_SPEED_100M,GPIO_PUPD_PU);
      GPIO_Set(GPIOF,PIN6|PIN11,GPIO_MODE_OUT,GPIO_OTYPE_OD,GPIO_SPEED_100M,GPIO_PUPD_PU);
      GPIO_Set(GPIOG,PIN6|PIN7|PIN8|PIN12|PIN15,GPIO_MODE_OUT,GPIO_OTYPE_OD,GPIO_SPEED_100M,GPIO_PUPD_PU);
            WriteByteToLCD(byte);//写
            delay_us(5);
           
            LCD_EN = 1;
            delay_us(5);
            LCD_EN = 0;         
         }
}
按照时序完成这个,基本上不会有太大问题了。剩下的就是初始化之类的,完整显示字符串和汉字实现见附件。
到这,基本的驱动算是完成了,但我想用液晶做人机界面及交互,必然会涉及到GUI,因此,底层必须实现画点函数。基于此,我完成了这个画点函数,下面就说一下我在实现这个函数过程中碰到的问题和解决办法。首先,由于LCD12864并没有画点指令,但好在还有一个256*64的DGRAM缓冲区,缓冲区和液晶位置映射图如下:
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg
下图即为GDRAM图
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg
我初步的想法是,直接写入相关位置实现,但是这样做存在一个弊端,那就是在写入某行多个点的时候后边的点会覆盖前边的点,为了避免这种情况,我们需要在写入某点之前,将当前的位址的所有点读出,然后将数据或上当前某点,然后再写入该位址。即所谓的读->改->写。具体实现如下:
//画点函数
/*
                            GDRAM分布
                  256(16*16)                                         X
  |-----------------------------|-----------------------------------|
   |0                            |                                   |
   |     上半屏128*32           |      下半屏128*32                 |
  Y|32                          |                                   |
  |-----------------------------|-----------------------------------|
水平x坐标每隔16个点才有一个位址,因此改变某个点需要知道该点位于这16个点的哪个位置
垂直y坐标在大于31时处于下半屏,小于31处于上半屏
*/
voidLCD_SetPoint(uint8_t x,uint8_t y)
{
    uint16_t volatile  readdata=0;
    //判断处于哪行哪列
         uint8_t x_pos,x_bit;//x_pos用来判断处于位址,x_bit用来判断处于位址中的位置
    uint8_t y_pos,y_bit;//y_pos用来判断处于上半屏还是下半屏 y_bit用来判断位于哪行              
         y_pos=y/32;   //0:上半屏;1:下半屏
         y_bit=y%32;   //得到具体行位置
         x_pos=x/16;
         x_bit=x%16;
         WriteToLCD(LCD_CMD,LCD_EXTERN_SET);//扩展指令集
    WriteToLCD(LCD_CMD,LCD_DRAW_OFF);//关掉绘图显示
         WriteToLCD(LCD_CMD,0x80+y_bit);
         WriteToLCD(LCD_CMD,0x80+8*y_pos+x_pos);
          //数据线IO方向设定
   GPIO_Set(GPIOE,PIN6,GPIO_MODE_IN,0,0,GPIO_PUPD_PD);
    GPIO_Set(GPIOF,PIN6|PIN11,GPIO_MODE_IN,0,0,GPIO_PUPD_PD);
   GPIO_Set(GPIOG,PIN6|PIN7|PIN8|PIN12|PIN15,GPIO_MODE_IN,0,0,GPIO_PUPD_PD);
         delay_ms(1);
    ReadByteFromLCD();//空读一次
         readdata=ReadByteFromLCD();
         readdata<<=8;
         readdata|=ReadByteFromLCD();
         WriteToLCD(LCD_CMD,0x80+y_bit);
         WriteToLCD(LCD_CMD,0x80+8*y_pos+x_pos);     
         if(x_bit<8)
         {
          WriteToLCD(LCD_DATA,((uint8_t)(readdata>>8))|(0x01<<(7-x_bit)));
          WriteToLCD(LCD_DATA,(uint8_t)readdata);
         }else
         {
          WriteToLCD(LCD_DATA,(uint8_t)(readdata>>8));
          WriteToLCD(LCD_DATA,(uint8_t)readdata|(0x01<<(15-x_bit)));
         }
         WriteToLCD(LCD_CMD,LCD_DRAW_ON);//开绘图显示
    WriteToLCD(LCD_CMD,LCD_BASIC_SET);//回到基本指令集
}
voidLCD_Refresh_GRAM(void)
{
     uint8_t i;uint16_t j;
          WriteToLCD(LCD_CMD,LCD_EXTERN_SET);//扩展指令集
     WriteToLCD(LCD_CMD,LCD_DRAW_OFF);//关掉绘图显示
          for(i=0;i<32;i++)//遍历0-31行
          {
       WriteToLCD(LCD_CMD,0x80+i);//写入行地址
           WriteToLCD(LCD_CMD,0x80);  //写入列地址
       for(j=0;j<32;j++)
       {
             WriteToLCD(LCD_DATA,LCD_GRAM[j]);
           }           
          }      
         WriteToLCD(LCD_CMD,LCD_DRAW_ON);//开启绘图显示
         WriteToLCD(LCD_CMD,LCD_BASIC_SET);//回到基本指令集      
  
}
这样做理论上没有问题,但在实际操作的时候出现了一些错误,在读取位址的时候,将IO口设置为上拉输入,读取到的数据都是0xff,设置为下拉输入,则读到的数据都是0x00。
因此,该实现就卡死在了这里。后来也曾试过将IO口设置为开漏输出,加上拉。在读取数据前,向端口位写1,然后读回数据的方法,但是也不能解决问题。
经过一番的查找思路的过程,最终想到了用MCU内部缓冲区的办法。这种方案需要MCU内存不能太紧张,不过相对STM32F407ZGT6那192K的内存,这点开销毛毛雨了。于是乎在内存中开辟了一块缓冲区:
实现一个[32][32]RAM缓冲区
格式如下
//[0]0 1 2 3 ...31(字节)   
//[1]0 1 2 3 ...31(字节)
//[2]0 1 2 3 ...31(字节)   
//[3]0 1 2 3 ...31(字节)
[0..31]代表0-31行,0..31代表某行的所有字节(16*16/8=32)。
画点函数实现纯粹是操作uint8_t LCD_GRAM[32][32];
这个二维数组,在操作完毕后,更新缓冲区数据到GDRAM中即可。
//x:0-127
//y:0-63
//mode:1,画点
//mode:0,清空
void LCD_DrawPoint(uint8_t x,uint8_t y,uint8_t mode)
{
   //判断处于哪行哪列
         uint8_tx_pos,x_bit;//x_pos用来判断处于位址,x_bit用来判断处于位址中的位置
   uint8_t y_pos,y_bit;//y_pos用来判断处于上半屏还是下半屏 y_bit用来判断位于哪行
//      uint8_tx_pos_temp;
   if((x>127)||(y>63)||(x<0)||(y<0))return;//去掉不合理参数
         y_pos=y/32;   //0:上半屏;1:下半屏
         y_bit=y%32;   //得到具体行位置
         x_pos=x/16;
         x_bit=x%16;
   if(y_pos>0)//下半屏
         {
                     if(mode)
                    {
                            if(x_bit<8)
                            {
                             LCD_GRAM[y_bit][x_pos*2+16]|=(1<<(7-x_bit));
                              LCD_GRAM[y_bit][x_pos*2+1+16]|=0x00;
                            }else
                            {
                              LCD_GRAM[y_bit][x_pos*2+16]|=0x00;
                             LCD_GRAM[y_bit][x_pos*2+1+16]|=(1<<(15-x_bit));
                            }
                    }
                   else
                   {        
                            if(x_bit<8)
                            {
                             LCD_GRAM[y_bit][x_pos*2+16]&=~(1<<(7-x_bit));
                              LCD_GRAM[y_bit][x_pos*2+1+16]&=~0x00;
                            }else
                            {
                              LCD_GRAM[y_bit][x_pos*2+16]&=~0x00;
                             LCD_GRAM[y_bit][x_pos*2+1+16]&=~(1<<(15-x_bit));
                            }
                   }
         }else//上半屏
         {
                    if(mode)
                    {
                            if(x_bit<8)
                            {
                              LCD_GRAM[y_bit][x_pos*2]|=(1<<(7-x_bit));
                              LCD_GRAM[y_bit][x_pos*2+1]|=0x00;
                            }else
                            {
                              LCD_GRAM[y_bit][x_pos*2]|=0x00;
                             LCD_GRAM[y_bit][x_pos*2+1]|=(1<<(15-x_bit));
                            }
                    }
                   else
                   {        
                            if(x_bit<8)
                            {
                             LCD_GRAM[y_bit][x_pos*2]&=~(1<<(7-x_bit));
                              LCD_GRAM[y_bit][x_pos*2+1]&=~0x00;
                            }else
                            {
                              LCD_GRAM[y_bit][x_pos*2]&=~0x00;
                             LCD_GRAM[y_bit][x_pos*2+1]&=~(1<<(15-x_bit));
                            }
                    }
         }
//           LCD_Refresh_GRAM();
}
这个函数是为最终的画点函数实现。调用此函数完毕后,再调用刷新缓冲区数据函数即可完成画点。
void LCD_Refresh_GRAM(void)
{
    uint8_t i;uint16_t j;
          WriteToLCD(LCD_CMD,LCD_EXTERN_SET);//扩展指令集
    WriteToLCD(LCD_CMD,LCD_DRAW_OFF);//关掉绘图显示
          for(i=0;i<32;i++)//遍历0-31行
          {
      WriteToLCD(LCD_CMD,0x80+i);//写入行地址
            WriteToLCD(LCD_CMD,0x80);  //写入列地址
      for(j=0;j<32;j++)
      {
              WriteToLCD(LCD_DATA,LCD_GRAM[j]);  
            }         
          }      
         WriteToLCD(LCD_CMD,LCD_DRAW_ON);//开启绘图显示
         WriteToLCD(LCD_CMD,LCD_BASIC_SET);//回到基本指令集      
  
}
具体的代码请参看附件我的工程,下面附上一张调试完成的照片,仅作观赏之用。
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg

STM32F407驱动12864.rar

8.69 MB, 下载次数: 1506

此帖出自stm32/stm8论坛
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

板凳
 

驱动4*4矩阵键盘

本帖最后由 人民币的幻想 于 2015-4-16 10:32 编辑

矩阵键盘其实早就在51、STM32F1平台写过了,因此这个驱动写起来很快。只要了解了思路以后,程序so easy~。思路如下:
先扫描行,在扫描行的时候,列输出0,行上拉输入,一旦某行有按键被按下,那么按键会被导通,然后电平会被拉低,因此,读回数据中的0的位置即为被按下按键所在行的位置。
再扫描列,过程同扫描行,只不过列上拉输入,行输出0,然后回读数据,最终经过计算判断是哪个按键被按下,整个矩阵键盘扫描就完成了。
/***************************************************************************************
4*4矩阵键盘引脚连接
列1-4 PB6  PB7 PA0 PC7
行1-4 PC13 PD6 PD7 PE2
***************************************************************************************/   
uint8_t KEYBoard_LRScan(void)
{
   uint8_t key=0;
         uint8_ttemp=0;
         KEYBoard_LRInit_LScan();//扫描行
         temp=(KEYBoard_GetInputVal()&0x0f);
         if(temp==0x0e)
         {
            key=1;//第一行有键被按下
         }else
         if(temp==0x0d)
         {
            key=2;//第二行有键被按下
         }else
         if(temp==0x0b)
         {
            key=3;
         }else
         if(temp==0x07)
         {
            key=4;
         }
         elsekey=0;
         KEYBoard_LRInit_RScan();//扫描列
         temp=(KEYBoard_GetInputVal()&0xf0);
         if((temp==0xe0)&&(key!=0))
         {
            key=(key-1)*4+1;
         }else
  if((temp==0xd0)&&(key!=0))
   {
     key=(key-1)*4+2;
  }else
  if((temp==0xb0)&&(key!=0))
   {
     key=(key-1)*4+3;
  }else
  if((temp==0x70)&&(key!=0))
   {
     key=(key-1)*4+4;
   }
  else key=0;
  return key;
}
出于按键手感考虑,我换用了12*12*7的实体按键,自己焊接了矩阵键盘,

同样的,我也把碰到的问题写一下:
程序按如上思路写完以后,键盘不能正常工作,按键值只有3,7,11,15。也就是说按下任一行,只有第三列的按键响应,我查看了下代码,没有发现问题,然后看了一下第三列的原理图连接,发现探索者板子在这个脚上接了MPU6050的中断输出引脚,并且加了一个1K电阻,于是果断把第三列引脚换到PA0口,再次编译下载验证,没有问题。

   



去抖是通过连续采集到20次一样的按键值做的,   
keyval_old=KEYBoard_LRScan();  
            
           if(keyval_old!=0)
           {  
                    keyval_new=KEYBoard_LRScan();
                    if((keyval_new!=0)&&(keyval_new==keyval_old))
                    {
                       key_counter++;
                    }
             if(key_counter==20)
             {
               key_counter=0;
                     while(KEYBoard_LRScan());
         printf("\r\nkeyval:%d\r\n",keyval_new);
              }
           }


STM32F407驱动4_4矩阵键盘.rar

8.28 MB, 下载次数: 70

此帖出自stm32/stm8论坛
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

4
 

【ADC采用】STM32F407ADC1双通道+DMA采用旋钮数值

本帖最后由 人民币的幻想 于 2015-4-23 15:28 编辑

毫无疑问,网络收音机需要频段音量调节,传统的按键调节起来感觉太难受,于是想到了旋钮的方式。于是动手做了一个旋钮模块,如下图。


为了固定方便,顺带打了2个定位孔,M3的螺丝刚刚好。言归正传,为了检测到旋钮的位置,需要将其变化转换为电压值,而完成这一转换的自然是ADC了,由于STM32F407已经内置了ADC,所以不需要再外置ADC芯片,本次实验使用的ADC1的通道4和通道5,即PA4和PA5,为了更加有效率的实现采样,我用来DMA方式实现,如下
/***************************************************************************************
旋钮输出模拟量,需要AD转换得到数字量
左:PA4  ADC12_IN4
右:PA5  ADC12_IN5
2015年4月16日完成
ADC1通道4和通道5使用DMA方式
DMA数据流0通道0是ADC1
ADC1_DR=ADC1_BASE+DR_OFFSET(0x4C)
ADC1_BASE=(APB2PERIPH_BASE + 0x2000)
APB2PERIPH_BASE=(PERIPH_BASE + 0x00010000)
PERIPH_BASE=((uint32_t)0x40000000)
计算ADC1_DR=0x4001204C
   ADC3_DR=0x4001224C
***************************************************************************************/
__IO uint16_t ADCConvertedValue[2];
void Button_Init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;
         ADC_CommonInitTypeDefADC_CommonInitStructure;
         ADC_InitTypeDef       ADC_InitStructure;
         DMA_InitTypeDef       DMA_InitStructure;
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_DMA2,ENABLE);//PA时钟
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC1时钟
         
         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5;
         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN;//模拟输入
         GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
         GPIO_Init(GPIOA,&GPIO_InitStructure);
         
   DMA_InitStructure.DMA_Channel=DMA_Channel_0;
   DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)ADC1_DR_ADDRESS;
   //ADCConvertedValue[0]存放ADC1通道4转换结果
   DMA_InitStructure.DMA_Memory0BaseAddr=(uint32_t)&ADCConvertedValue;         
   DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralToMemory;
   DMA_InitStructure.DMA_BufferSize=2;
   DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
   DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
   DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;
   DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
   DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;        
   DMA_InitStructure.DMA_Priority=DMA_Priority_High;
   DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;
   DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_HalfFull;
   DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;
   DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;
   DMA_Init(DMA2_Stream0,&DMA_InitStructure);
   DMA_Cmd(DMA2_Stream0,ENABLE);
         //独立模式
         ADC_CommonInitStructure.ADC_Mode=ADC_Mode_Independent;
         //2个采样阶段延迟5个时钟周期
         ADC_CommonInitStructure.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_5Cycles;
         //预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz
         ADC_CommonInitStructure.ADC_Prescaler=ADC_Prescaler_Div4;
         //DMA失能
         ADC_CommonInitStructure.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled;
         ADC_CommonInit(&ADC_CommonInitStructure);
         
         ADC_InitStructure.ADC_Resolution= ADC_Resolution_12b;//12位模式
   ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描模式   
   ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//开启连续转换
   ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发
   ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
   ADC_InitStructure.ADC_NbrOfConversion = 2;//2个转换在规则序列中 也就是只转换规则序列1
   ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
         ADC_DMACmd(ADC1,ENABLE);
         ADC_RegularChannelConfig(ADC1,ADC_Channel_4, 1, ADC_SampleTime_144Cycles );
   ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_15Cycles);      
         //转换完成使能DMA请求
         ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE);
         
     ADC_Cmd(ADC1, ENABLE);//开启AD转换器
         ADC_SoftwareStartConv(ADC1);
         
}
注意要点:DMA方向配置不要出错,还有保存数据的数组大小及类型
   DMA_InitStructure.DMA_Memory0BaseAddr=(uint32_t)&ADCConvertedValue;         
   DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralToMemory;
   DMA_InitStructure.DMA_BufferSize=2;
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
剩下的就是,在主函数里初始化这个函数,然后从ADCConvertedValue[]数组取数了,可以看到,电压采集还是比较精确的。
模块和主板连接好以后的样子如下图:

硬件平台全家福


YP20150416215229149.jpeg(1).JPG (26.18 KB, 下载次数: 0)

YP20150416215229149.jpeg(1).JPG

YP20150416215446770.jpeg.JPG (26.32 KB, 下载次数: 0)

YP20150416215446770.jpeg.JPG

YP20150416215726902.jpeg.JPG (23.89 KB, 下载次数: 0)

YP20150416215726902.jpeg.JPG

ADC1通道4和5通过DMA得到旋钮当前值.rar

8.58 MB, 下载次数: 54

此帖出自stm32/stm8论坛
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

5
 

【LCD显示文件长名】STM32F407平台移植znFAT读取SD卡汉字库实现中文长名文件显示

本帖最后由 人民币的幻想 于 2015-4-23 16:07 编辑

znFAT移植比较简单,首先将自己的SD卡初始化函数放到如下函数里
UINT8 znFAT_Device_Init(void)
{
UINT8 res=0,err=0;
ioctl.just_dev=0;
ioctl.just_sec=0;
//以下为各存储设备的初始化函数调用,请沿袭以下格式
res=SD_Init();
if(res) err|=0X01;
//res=Device1_Init();
//if(res) err|=0X02;
return err; //返回错误码,如果某一设备初始化失败,则err相应位为1
}
其次,按照如下格式将自己的SD卡读写扇区函数写好就行了。
UINT8 znFAT_Device_Read_Sector(UINT32addr,UINT8 *buffer)
{
if(buffer==znFAT_Buffer) //如果是针对znFAT内部缓冲区的操作
{                        
if(ioctl.just_dev==Dev_No  //如果现在要读取的扇区与内部缓冲所对应的扇区(即最近一次操作的扇区)是同一扇区
    && (ioctl.just_sec==addr && 0!=ioctl.just_sec)) //则不再进行读取,直接返回
{                                          
  return 0;      
  }
else //否则,就将最近一次操作的扇区标记为当前扇区
  {
  ioctl.just_dev=Dev_No;
  ioctl.just_sec=addr;
  }
}
switch(Dev_No) //有多少个存储设备,就有多少个case分支
{
case 0:
          while(SD_ReadDisk(buffer,addr,1));
           break;
//case 1:
//      while(SD2_Read_Sector(addr,buffer));
//       break;
//case...
  
}
return 0;
}
UINT8znFAT_Device_Write_Sector(UINT32 addr,UINT8 *buffer)
{
if(buffer==znFAT_Buffer) //如果数据缓冲区是内部缓冲
{
  ioctl.just_dev=Dev_No; //更新为当前设备号
  ioctl.just_sec=addr; //更新为当前操作的扇区地址   
}
switch(Dev_No)
{
  case 0:
         while(SD_WriteDisk(buffer,addr,1));
           break;
  //case 1:
//     while(SD2_Write_Sector(addr,buffer));
//       break;
  //case...
  
}
return 0;
}
到此,znFAT文件系统就移植成功了,接下来这部分是长名显示需要做的步骤。
1. 首先判断文件是否存在长名
2. 存在长名,则将长名UNI码转换为UNI串,
void hexstring2string(struct FileInfo FileInfo,u8* dstbuf)
{
  uint8_t i=0;uint16_t j=0;
  while(FileInfo.longname!=0)
  {
     dstbuf[j]=text_hex2chr((FileInfo.longname>>12)&0x0F);
   dstbuf[j+1]=text_hex2chr((FileInfo.longname>>8)&0x0F);
   dstbuf[j+2]=text_hex2chr((FileInfo.longname>>4)&0x0F);
   dstbuf[j+3]=text_hex2chr((FileInfo.longname>>0)&0x0F);
    i++;j+=4;
    }   
  dstbuf[j]=0;
}
3. 然后将UNI串转换为GBK码
//src:输入字符串
//dst:输出字符串(unigbk时为gbk内码,gbk2uni时为uni字符串)
//mode:0,uni2gbk
//     1,gbk2uni
void  unigbk_exchange(u8*src,u8* dst,u8 mode)
{
   u16 temp;
   u8  buf[2];
   if(mode)//gbk2uni
   {
       while(*src!='\0')
     {
        if(*src<0x81)//并不是汉字,因为汉字GBK码最小为0x8140
        {
           temp=(u16)unigbk((unsigned short)*src,1);
          src++;
        }else//为汉字 占用2个字节
       {
          buf[1]=*src++;
         buf[0]=*src++;
         //将某个汉字转换为了uni码
         temp=(u16)unigbk((unsignedshort)*(u16*)buf,1);
       }
      *dst++=text_hex2chr((temp>>12)&0x0F);
      *dst++=text_hex2chr((temp>>8)&0x0F);
       *dst++=text_hex2chr((temp>>4)&0x0F);
       *dst++=text_hex2chr((temp)&0x0F);
     }
   }
   else//uni2gbk
   {
      while(*src!='\0')
    {
        buf[1]=text_chr2hex(*src++)*16;
       buf[1]+=text_chr2hex(*src++);
       buf[0]=text_chr2hex(*src++)*16;
       buf[0]+=text_chr2hex(*src++);
       temp=(u16)unigbk((unsignedshort)*(u16*)buf,0);
        if(temp<0x80)
          {
          *dst=temp;dst++;
        }else//需要交换一下高低字节,得到正确汉字
       {
         *(u16*)dst=swap16(temp);
        dst+=2;
       }
   
    }
   }
  *dst=0;
}
4. 通过以上2个函数的使用,我们已经可以在串口助手看到长名数据了,但是我的目的是实现长名文件在LCD上的显示,这就需要花点功夫了,怎么做呢?我们经过unigbk_exchange函数得到了长名文件的GBK码,那么下一步就是根据GBK码得到其对应的汉字点阵,得到点阵后根据点阵数据在LCD上打点,然后刷新缓冲区就可以显示汉字了。PS:知道当初为什么非得把画点函数写好了吧~.~
//code 字符指针开始
//从字库中查找出字模
//code 字符串的开始地址,GBK码
//mat  数据存放地址 (size/8+((size%8)?1:0))*(size) bytes大小  
//size:字体大小
void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size)
{   
   unsigned char qh,ql;
  unsigned char i;            
  unsigned long foffset;
  u8csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数  
  qh=*code;
  ql=*(++code);
  if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非 常用汉字
  {            
      for(i=0;i
      return; //结束访问
  }         
  if(ql<0x7f)ql-=0x40;//注意!
  else ql-=0x41;
  qh-=0x81;   
  foffset=((unsignedlong)190*qh+ql)*csize; //得到字库中的字节偏移量      
// printf("foffset:%ld",foffset);
  switch(size)
  {  
     case12:znFAT_ReadData(&fileinfo_f12,foffset,csize,mat);
             break;   
        case16:znFAT_ReadData(&fileinfo_f16,foffset,csize,mat);
             break;
        case24:znFAT_ReadData(&fileinfo_f24,foffset,csize,mat);
              break;            
  }                                 
   
}
//显示一个指定大小的汉字
//x,y :汉字的坐标
//font:汉字GBK码
//size:字体大小   
void Show_Font(unsigned char x,unsigned char y,u8 *font,u8 size)
{
  u8 temp,t,t1;
  u16 y0=y;
  u8 dzk[72];   
  u8csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数  
  if(size!=12&&size!=16&&size!=24)return; //不支持的size
  Get_HzMat(font,dzk,size); //得到相应大小的点阵数据
  for(t=0;t
  {                                       
     temp=dzk[t];        //得到点阵数据      
     for(t1=0;t1<8;t1++)
     {
        if(temp&0x80)
        LCD_DrawPoint(x,y,1);
        temp<<=1;
        y++;
        if((y-y0)==size)
        {
           y=y0;
           x++;
           break;
        }
     }      
  }  
// LCD_Refresh_GRAM();
}
5. 单单显示一个字符当然不过瘾,需要在LCD上实现字符串的显示,基于此,我编写了这个函数,12、16、24字体自动换行显示。
//在指定位置开始显示一个字符串      
//(x,y):起始坐标
//str  :字符串
//size :字体大小               
void Show_Str(unsigned char x,unsigned char y,u8*str,u8 size)
{            
  uint8_t i=0;
  uint8_t isHZ=0;
  if(x>127||y>63)return;
    while(str!='\0')//判断数据是否结束
  {
     if(!isHZ)
     {
         if(str>0x80)isHZ=1;//中文
            else                  //字符
         {
          if(x>127-11)
          {
              x=0;
             if(size!=24)
              y+=16;
             else            
              y+=24;
          }
          if(size!=24)
          {
             if(y>63-11)
             {     
               y=0;
               LCD_Clear_DGRAM();
             }
          }else
          {
             if(y>63-23)
             {
               y=0;
               LCD_Clear_DGRAM();
             }
          }
          LCD_ShowChar(x,y,*(str+i),size,1);
          x+=size;
          i++;
        }      
        
     }else//中文
       {
           isHZ=0;
          if(x>127-11)
          {
              x=0;
             if(size!=24)
              y+=16;
             else            
              y+=24;
          }
          if(size!=24)
          {
             if(y>63-11)
             {
               y=0;
               LCD_Clear_DGRAM();
             }
          }else
          {
             if(y>63-23)
             {
              y=0;
               LCD_Clear_DGRAM();
             }
          }
          Show_Font(x,y,str+i,size);
          x+=size;
          i+=2;  
     }
  }
  
}  
到此,整个长名显示的步骤都完成了,具体实现请看我的代码工程。

长文件名显示.rar

9.78 MB, 下载次数: 55

此帖出自stm32/stm8论坛
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 
 

回复

5229

帖子

236

TA的资源

管理员

6
 
快5月了开始做毕设,哈哈
楼主抓紧啊
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
 
 

回复

375

帖子

0

TA的资源

一粒金砂(高级)

7
 
坐等楼主更新
此帖出自stm32/stm8论坛
 
 
 

回复

29

帖子

0

TA的资源

一粒金砂(中级)

8
 
楼主很强大。
此帖出自stm32/stm8论坛
 
 
 

回复

7504

帖子

2

TA的资源

五彩晶圆(高级)

9
 
为何不考虑wifi? 拖一根网线的收音机不好玩啊
此帖出自stm32/stm8论坛

点评

兜里没米了,搞不起sdio wifi了。  详情 回复 发表于 2015-4-14 16:35
 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 

回复

280

帖子

0

TA的资源

一粒金砂(高级)

10
 
跟着楼主学习了!rt-thread有个项目做这个的。
此帖出自stm32/stm8论坛

点评

对,我的思路就是他们的项目。但是我换了平台和方案实现。  详情 回复 发表于 2015-4-14 16:32
 
 
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

11
 
flyword 发表于 2015-4-14 16:17
跟着楼主学习了!rt-thread有个项目做这个的。

对,我的思路就是他们的项目。但是我换了平台和方案实现。
此帖出自stm32/stm8论坛
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

12
 
freebsder 发表于 2015-4-14 16:05
为何不考虑wifi? 拖一根网线的收音机不好玩啊

兜里没米了,搞不起sdio wifi了。
此帖出自stm32/stm8论坛

点评

这wifi多少钱?  详情 回复 发表于 2015-4-15 09:41
搞个wifi的带劲啊,哈哈。强烈建议啊!!  详情 回复 发表于 2015-4-15 09:06
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 
 

回复

280

帖子

0

TA的资源

一粒金砂(高级)

13
 
人民币的幻想 发表于 2015-4-14 16:35
兜里没米了,搞不起sdio wifi了。

搞个wifi的带劲啊,哈哈。强烈建议啊!!
此帖出自stm32/stm8论坛
 
 
 

回复

2万

帖子

71

TA的资源

管理员

14
 
人民币的幻想 发表于 2015-4-14 16:35
兜里没米了,搞不起sdio wifi了。

这wifi多少钱?
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

点评

100大洋左右  详情 回复 发表于 2015-4-15 10:32
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

15
 
soso 发表于 2015-4-15 09:41
这wifi多少钱?

100大洋左右
此帖出自stm32/stm8论坛

点评

做吧 完全分享完 我们给报销 可能还有额外奖励哈  详情 回复 发表于 2015-4-15 10:34
 
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 
 

回复

2万

帖子

71

TA的资源

管理员

16
 

做吧  完全分享完 我们给报销  可能还有额外奖励哈
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

点评

还准备把庆科的wifi支持给楼主的 毕业季的时候搜罗支持一些这种开源毕设 听上去很牛x啊  详情 回复 发表于 2015-4-15 14:51
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复

3415

帖子

0

TA的资源

纯净的硅(高级)

17
 
soso 发表于 2015-4-15 10:34
做吧  完全分享完 我们给报销  可能还有额外奖励哈


还准备把庆科的wifi支持给楼主的


毕业季的时候搜罗支持一些这种开源毕设

听上去很牛x啊
此帖出自stm32/stm8论坛

点评

给力的版主,要不你发起活动,我们来支持一些奖品吧。  详情 回复 发表于 2015-4-15 15:00
 
个人签名

So TM what......?

 

 

回复

2万

帖子

71

TA的资源

管理员

18
 
ljj3166 发表于 2015-4-15 14:51
还准备把庆科的wifi支持给楼主的


毕业季的时候搜罗支持一些这种开源毕设

听上去很牛x啊

给力的版主,要不你发起活动,我们来支持一些奖品吧。
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

点评

今年估计是赶不上了 貌似本月底,下月初就开始陆陆续续答辩了 话说回来,有潜力、有意思的开源毕设,真的可以考虑支持一下 毕竟是非做不可的  详情 回复 发表于 2015-4-15 15:08
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复

3415

帖子

0

TA的资源

纯净的硅(高级)

19
 
soso 发表于 2015-4-15 15:00
给力的版主,要不你发起活动,我们来支持一些奖品吧。

今年估计是赶不上了

貌似本月底,下月初就开始陆陆续续答辩了



话说回来,有潜力、有意思的开源毕设,真的可以考虑支持一下

毕竟是非做不可的
此帖出自stm32/stm8论坛
 
个人签名

So TM what......?

 

 

回复

2万

帖子

71

TA的资源

管理员

20
 
嗯 是的  嘿嘿 那下次咱提早筹备   嘿嘿  
此帖出自stm32/stm8论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表