STM32实战总结:HAL之SDIO

在介绍SDIO接口之前先了解一下MMC、SD卡、SD标准等背景知识。

MMC(Multi Media Card):即多媒体卡,它是一种非易失性存储器件,体积小巧,容量大,耗电量低,传输速度快,主要应用于消费类电子产品中;

SD(Secure Digital Memory Card):即安全数码卡,它是在MMC卡的基础上发展而来,并增加两个主要特色:SD卡增加了数据的安全访问,可以设定对存储数据的访问权限,防止数据被他人复制;SD相比2.11版的MMC卡传输速度更快;SD卡向前兼容MMC卡,所有支持SD卡的设备也支持MMC卡;

SD 标准,即针对SD卡的标准化指定的对应标准,使制造商能够提供高性能产品,以增强每天听音乐、录制视频、拍照、保存数据和使用手机的数百万人的体验。作为行业标准,SD 标准被用于便携式存储行业的多个细分市场,包括手机、数码相机、MP3 播放器、个人电脑、平板电脑、打印机、汽车导航系统、电子书和许多其他消费电子设备。

SDIO

SDIO是在SD标准上定义的一种外设接口,和SD规范的唯一区别是增加了低速标准。

SDIO卡只需要使用SPI或1位SD传输模式支持低速模式,低速卡的应用目标是以最小的硬件开销支持低速IO能力,低速卡支持类似调制解调器、条码扫描仪和GPS接收器等应用。      

目前常见的SDIO外围有:SD卡、多媒体卡(MMC卡),TF卡等等,另外随着SDIO硬件设备的扩充,SDIO总线的外围能够支持更多的SDIO设备比如bluetooth,wifi,GPS,camera sensor等,它们的识别过程跟SD卡类似,主要差别是在SD协议的基础上做了些扩展。

SDIO总线和USB总线类似,SDIO总线也有两端,其中一端是主机(HOST)端,另一端是设备端(DEVICE),采用HOST- DEVICE这样的设计是为了简化DEVICE的设计,所有的通信都是由HOST端发出命令开始的。在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了,SDIO的HOST可以连接多个DEVICE。

图中SDIO控制器有两组SDIO总线,可以挂两张SDIO card。
SDIO总线pin脚:
1,CLK信号:HOST给DEVICE的时钟信号线
2,CMD信号:用于HOST发送命令和DEVICE回复命令
3,DAT0-DAT3:用于传送的数据线(DAT1复用作中断和数据传输)

另外还有
4,VDD信号:电源信号
5,VSS1,VSS2:电源地信号

共9个引脚。

SDIO有三种传输模式

SPI传输模式
1Bit传输模式
4Bit传输模式

SDIO协议规定,在SDIO的1bit模式下,数据线DATA0用来传输数据,DATA1用作中断。在SDIO的4bit模式下,数据线DATA0~3用于传输数据,其中DATA1复用作中断线;

以SD卡和TF卡为例,查看它们的pin脚定义

SD存储卡( Secure Digital Memory Card),TF卡( Micro SD Card,原名Trans-flash Card),他们数据传输都同是通过SDIO总线,我们可以称它为SD card,虽然都用的是SDIO总线,SD card在HOST识别的过程中使用的是SD规范,SDIO card与之不同的是SDIO card识别使用的是SDIO规范。SDIO规范是从SD规范进行了扩展而来。SD卡比TF卡多了一个VSS引脚,他们都支持SD 1bit,4bit模式,和SPI模式,模式不同pin脚的功能也不一样。
 

SDIO命令

SDIO总线上的设置和控制都是通过命令来实现,SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求,其中请求和应答中会包含数据信息:

  • Command: 用于开始传输的命令,是由HOST端发往DEVICE端的,其中命令是通过CMD信号线传送的。
  • Response: DEVICE返回的应答。也是通过CMD线传送的;
  • Data: 数据是双向传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。

SDIO的命令分为:应用相关命令(ACMD)和通用命令(CMD)两部分。发送ACMD时,需先发送CMD55。

SDIO所有的命令和响应都是在SDIO_CMD引脚上面传输的,命令长度固定为48位,SDIO命令格式如下表所示:

SDIO的响应

一般SD卡在接收到命令行,都会有一个应答(CMD0例外),这个应答我们也称之为响应。STM32的SDIO接口,支持2种响应类型:短响应(48位)和长响应(136位)

SDIO短响应(48位)格式如下表所示:

SDIO长响应(136位)格式如下表所示:

SD卡总共有6类响应(R1、R1b、R2、R3、R6、R7),我们这里以R1为例简单介绍一下。R1(普通响应命令)响应属于短响应,其长度为48位,如下表所示:

STM32的SDIO

具体查看数据手册

本文不赘述。  

注意:

STM32的SDIO没有SPI兼容的通信模式;

MMC支持1/4/8位操作模式;

SD卡只支持1/4位操作模式;

复位后默认情况下SDIO_D0用于数据传输。初始化后主机可以改变数据总线的宽度。

如果SD或SD I/O卡接到了总线上,可通过主机配置数据传输使用SDIO_D0或SDIO_D[3:0]。所有的数据线都工作在推挽模式。

MX初始化

首先选择模式

分别为SD卡1位模式、4位模式;MMC卡1/4/8位模式。

常用的是SD 4 bits,即使用4根数据线来传输数据。

参考原理图如下:

更多参数设置:

在对参数进行解释前,先做几点相关说明:

SDIO模块使用了两个时钟,一个是用于AHB一侧的,一个是用于SD卡一侧的。

这两个时钟在时钟树中有所体现:

图中上方框里的是SD卡侧时钟,下方框是AHB接口时钟。

这里要注意的是,SDIO并非APB上的外设,而是直接跟AHB连接的接口。

接下来说明参数设置

从上到下:

✔在时钟上升沿还是下降沿进行位捕获,SDIO_CK是卡的时钟,每个时钟周期在命令和数据线上传输1位命令或数据;

✔是否启用时钟旁路,SDIO_CK输出可以使用时钟分频或时钟旁路模式,我们一般使用时钟分频,所以时钟旁路模式不使能;

直接旁路使用,则72M时钟用于SDIO_CK过快。对于SD卡来说,一般不超过25MHz

✔当总线空闲时是否使能时钟输出,这里主要是低功耗时考虑,即空闲时间是否关闭时钟;

✔硬件流控;

✔时钟分频因子,这里设置的是SDIO时钟引脚输出的时钟,不是时钟源

在下方有针对时钟分频因子的说明,分频因子在0—255之间,最终得到的时钟等于SD卡适配器侧时钟源除以(分频因子+2)

综上所述,参数一般默认即可。

如果时钟过快,也可根据实际情况进行适当分频。

关键代码

相关库函数:

stm32f1xx_hal_sd.h

/* Includes ------------------------------------------------------------------*/
#include "MyApplication.h"
#include "font_ASCII.h"
#include "font_CHN.h"
#include "Pic1.h"
#include "Pic2.h"

/* Private define-------------------------------------------------------------*/

/* Private variables----------------------------------------------------------*/
static void LCD_Init(void);                                                 //LCD屏幕初始化
static void LCD_FillColor(uint16_t,uint16_t,uint16_t,uint16_t,LCD_Color_t); //LCD屏幕填充颜色
static void LCD_ShowChar(uint16_t, uint16_t, const char, uint16_t, uint16_t,ASCII_font_t);     //在LCD屏幕上显示一个英文字符
static void LCD_ShowString(uint16_t, uint16_t, const char *, uint16_t, uint16_t,ASCII_font_t); //在LCD屏幕上显示英文字符串
static void LCD_ShowCHN(uint16_t, uint16_t, const char *, uint16_t, uint16_t,CHN_font_t);      //在LCD屏幕上显示一个中文字符
static void LCD_ShowCHNandENGstring(uint16_t, uint16_t, const char *, uint16_t, uint16_t,CHN_font_t,ASCII_font_t); //在LCD屏幕上显示中英文字符串
static void LCD_ShowPicture(uint16_t, uint16_t, uint16_t, uint16_t,uint8_t);                   //在LCD屏幕上显示图片
/* Public variables-----------------------------------------------------------*/
TFT_LCD_t TFT_LCD = 
{
	0,
	
	LCD_Init,
	LCD_FillColor,
	LCD_ShowChar,
	LCD_ShowString,
	LCD_ShowCHN,
	LCD_ShowCHNandENGstring,
	LCD_ShowPicture	
};

/* Private function prototypes------------------------------------------------*/
static uint32_t LCD_ReadID(void);                                //LCD读取ID
static void LCD_Disp_Direction(void);                            //LCD显示方向
static void LCD_SetWindows(uint16_t,uint16_t,uint16_t,uint16_t); //设置LCD显示窗口

/*
	* @name   LCD_ReadID
	* @brief  LCD读取ID
	* @param  None
	* @retval LCD_ID -> 返回LCD屏幕ID    
*/
static uint32_t LCD_ReadID(void)
{
	uint32_t LCD_ID = 0;
	uint32_t buf[4];
	
	LCD_Write_CMD(0xD3);
	buf[0] = LCD_Read_DATA();        // 第一个读取数据无效
	buf[1] = LCD_Read_DATA()&0x00FF; // 只有低8位数据有效
	buf[2] = LCD_Read_DATA()&0x00FF; // 只有低8位数据有效
	buf[3] = LCD_Read_DATA()&0x00FF; // 只有低8位数据有效
	
	LCD_ID = (buf[1] << 16) + (buf[2] << 8) + buf[3];
	return LCD_ID;
}

/*
	* @name   LCD_Init
	* @brief  LCD屏幕初始化
	* @param  None
	* @retval None      
*/
static void LCD_Init(void)
{
	//读取LCD屏幕ID
	TFT_LCD.ID = LCD_ReadID();
	printf("The ID of TFT LCD is 0x%.6X\r\n",TFT_LCD.ID);
	
	//2.8inch ILI9341初始化
	LCD_Write_CMD(0xCF);  
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0xC9);   //C1 
	LCD_Write_DATA(0x30); 
	LCD_Write_CMD(0xED);  
	LCD_Write_DATA(0x64); 
	LCD_Write_DATA(0x03); 
	LCD_Write_DATA(0X12); 
	LCD_Write_DATA(0X81); 
	LCD_Write_CMD(0xE8);  
	LCD_Write_DATA(0x85); 
	LCD_Write_DATA(0x10); 
	LCD_Write_DATA(0x7A); 
	LCD_Write_CMD(0xCB);  
	LCD_Write_DATA(0x39); 
	LCD_Write_DATA(0x2C); 
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0x34); 
	LCD_Write_DATA(0x02); 
	LCD_Write_CMD(0xF7);  
	LCD_Write_DATA(0x20); 
	LCD_Write_CMD(0xEA);  
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0x00); 
	LCD_Write_CMD(0xC0);    //Power control 
	LCD_Write_DATA(0x1B);   //VRH[5:0] 
	LCD_Write_CMD(0xC1);    //Power control 
	LCD_Write_DATA(0x00);   //SAP[2:0];BT[3:0] 01 
	LCD_Write_CMD(0xC5);    //VCM control 
	LCD_Write_DATA(0x30); 	//3F
	LCD_Write_DATA(0x30); 	//3C
	LCD_Write_CMD(0xC7);    //VCM control2 
	LCD_Write_DATA(0XB7); 
	//LCD_Write_CMD(0x36);    // Memory Access Control 
	//LCD_Write_DATA(0x08); 
	LCD_Disp_Direction();   //设置LCD显示方向
	LCD_Write_CMD(0x3A);   
	LCD_Write_DATA(0x55); 
	LCD_Write_CMD(0xB1);   
	LCD_Write_DATA(0x00);   
	LCD_Write_DATA(0x1A); 
	LCD_Write_CMD(0xB6);    // Display Function Control 
	LCD_Write_DATA(0x0A); 
	LCD_Write_DATA(0xA2); 
	LCD_Write_CMD(0xF2);    // 3Gamma Function Disable 
	LCD_Write_DATA(0x00); 
	LCD_Write_CMD(0x26);    //Gamma curve selected 
	LCD_Write_DATA(0x01); 
	LCD_Write_CMD(0xE0);    //Set Gamma 
	LCD_Write_DATA(0x0F); 
	LCD_Write_DATA(0x2A); 
	LCD_Write_DATA(0x28); 
	LCD_Write_DATA(0x08); 
	LCD_Write_DATA(0x0E); 
	LCD_Write_DATA(0x08); 
	LCD_Write_DATA(0x54); 
	LCD_Write_DATA(0XA9); 
	LCD_Write_DATA(0x43); 
	LCD_Write_DATA(0x0A); 
	LCD_Write_DATA(0x0F); 
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0x00); 		 
	LCD_Write_CMD(0XE1);    //Set Gamma 
	LCD_Write_DATA(0x00); 
	LCD_Write_DATA(0x15); 
	LCD_Write_DATA(0x17); 
	LCD_Write_DATA(0x07); 
	LCD_Write_DATA(0x11); 
	LCD_Write_DATA(0x06); 
	LCD_Write_DATA(0x2B); 
	LCD_Write_DATA(0x56); 
	LCD_Write_DATA(0x3C); 
	LCD_Write_DATA(0x05); 
	LCD_Write_DATA(0x10); 
	LCD_Write_DATA(0x0F); 
	LCD_Write_DATA(0x3F); 
	LCD_Write_DATA(0x3F); 
	LCD_Write_DATA(0x0F); 
	LCD_Write_CMD(0x2B); 
	LCD_Write_DATA(0x00);
	LCD_Write_DATA(0x00);
	LCD_Write_DATA(0x01);
	LCD_Write_DATA(0x3f);
	LCD_Write_CMD(0x2A); 
	LCD_Write_DATA(0x00);
	LCD_Write_DATA(0x00);
	LCD_Write_DATA(0x00);
	LCD_Write_DATA(0xef);	 
	LCD_Write_CMD(0x11); //Exit Sleep
	HAL_Delay(120);
	LCD_Write_CMD(0x29); //display on		
  
	TFT_LCD_BL_ON;                  //打开背光
	TFT_LCD.FillColor(0,0,LCD_WIDTH,LCD_HEIGTH,Color_GRAY);  //屏幕填充灰色
}

/*
	* @name   LCD_Disp_Directio
	* @brief  LCD显示方向
	* @param  None
	* @retval None      
*/
static void LCD_Disp_Direction()
{
	switch(LCD_DIRECTION)
	{
		case 1:	LCD_Write_CMD(0x36); LCD_Write_DATA(1<<3); break;
		case 2: LCD_Write_CMD(0x36); LCD_Write_DATA((1<<3)|(1<<5)|(1<<6)); break;
		case 3: LCD_Write_CMD(0x36); LCD_Write_DATA((1<<3)|(1<<7)|(1<<4)|(1<<6)); break;
		case 4: LCD_Write_CMD(0x36); LCD_Write_DATA((1<<3)|(1<<7)|(1<<5)|(1<<4)); break;
		default:LCD_Write_CMD(0x36); LCD_Write_DATA(1<<3); break;			
	}
}

/*
	* @name   LCD_SetWindows
	* @brief  设置LCD显示窗口
	* @param  xStar  ->窗口的起点X坐标
						yStar  ->窗口的起点Y坐标
						xWidth ->窗口的宽度
						yHeight->窗口的高度
	* @retval None      
*/
static void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xWidth,uint16_t yHeight)
{	
	LCD_Write_CMD(LCD_CMD_SETxOrgin);	
	LCD_Write_DATA(xStar>>8);
	LCD_Write_DATA(0x00FF&xStar);		
	LCD_Write_DATA((xStar+xWidth-1)>>8);
	LCD_Write_DATA((xStar+xWidth-1)&0xFF);

	LCD_Write_CMD(LCD_CMD_SETyOrgin);
	LCD_Write_DATA(yStar>>8);
	LCD_Write_DATA(0x00FF&yStar);		
	LCD_Write_DATA((yStar+yHeight-1)>>8);
	LCD_Write_DATA((yStar+yHeight-1)&0xFF);

	LCD_Write_CMD(LCD_CMD_WRgram); //开始写入GRAM		
}	

/*
	* @name   LCD_FillColor
	* @brief  LCD屏幕填充颜色
	* @param  xStar  ->窗口的起点X坐标
						yStar  ->窗口的起点Y坐标
						xWidth ->窗口的宽度
						yHeight->窗口的高度 
						FillColor -> 填充色
	* @retval None      
*/
static void LCD_FillColor(uint16_t xStar, uint16_t yStar,uint16_t xWidth,uint16_t yHeight,LCD_Color_t FillColor)
{
	uint16_t i,j;
	//uint16_t k;
	
	//设置窗口
	LCD_SetWindows(xStar,yStar,xWidth,yHeight);
	//填充颜色
	for(i=xStar;i<(xStar+xWidth);i++)
	{
		for(j=0;j<(yStar+yHeight);j++)
		{
			LCD_Write_DATA(FillColor);
			//动态观看屏幕显示过程
			//for(k=0;k<100;k++);
		}
	}
}


/*
	* @name   LCD_ShowChar
	* @brief  在LCD屏幕上显示一个英文字符
	* @param  usX: 起始X坐标
*           usY :起始Y坐标
*           cChar :要显示的英文字符
*           usColor_Background :选择英文字符的背景色
*           usColor_Foreground :选择英文字符的前景色
*           font:字体选择
*             参数:ASCII_font_16 :16号字体
*                   ASCII_font_24 :24号字体 
	* @retval None      
*/
static void LCD_ShowChar(uint16_t usX, uint16_t usY, const char cChar, uint16_t usColor_Background, uint16_t usColor_Foreground,ASCII_font_t font)
{
	uint8_t ucTemp, ucIndex, ucPage, ucColumn;
  
  //检查输入参数是否合法
  assert_param(IS_ASCII_font(font));
  //ASCII字符集数组索引,需要减去偏移量(' ' -> 空格键的码值)
	ucIndex = cChar - ' ';
  
	//判断字体 - 16号字体
	if(font == ASCII_font_16)
	{
		//设置窗口,大小为8x16
		LCD_SetWindows(usX,usY,8,16);
		//逐行写入数据,共16行,每行8个像素点
		for(ucPage=0;ucPage<16;ucPage++)
		{
			//从ASCII字符集数组获取像素数据	
			//像素点数据为1时,写入字符颜色,为0时,写入背景颜色
			ucTemp = ucAscii_1608[ucIndex][ucPage];
			for(ucColumn=0;ucColumn<8;ucColumn++)
			{
				if((ucTemp & BIT0) == BIT0)
					LCD_Write_DATA(usColor_Foreground);			
				else
					LCD_Write_DATA(usColor_Background);								
				ucTemp >>= 1;					
			}
		} 
	}
	//判断字体 - 24号字体
	if(font == ASCII_font_24)
	{
		//设置窗口,大小为12x24
		LCD_SetWindows(usX,usY,12,24);
		//逐行写入数据,共24行,每行12个像素点(占2个字节)
		for(ucPage=0;ucPage<48;ucPage+=2)
		{
			//从ASCII字符集数组获取像素数据,前8个像素点
			//像素点数据为1时,写入字符颜色,为0时,写入背景颜色
			ucTemp = ucAscii_2412[ucIndex][ucPage];
			for(ucColumn=0;ucColumn<8;ucColumn++)
			{
				if((ucTemp & BIT0) == BIT0)
					LCD_Write_DATA(usColor_Foreground);			
				else
					LCD_Write_DATA(usColor_Background);								
				ucTemp >>= 1;					
			}
			//从ASCII字符集数组获取像素数据,后4个像素点
			//像素点数据为1时,写入字符颜色,为0时,写入背景颜色
      ucTemp=ucAscii_2412[ucIndex][ucPage+1];
			for(ucColumn=0;ucColumn<4;ucColumn++)
			{
				if((ucTemp & BIT0) == BIT0)
					LCD_Write_DATA(usColor_Foreground);			
				else
					LCD_Write_DATA(usColor_Background);								
				ucTemp >>= 1;					
			}
		} 
	}
}

/*
	* @name   LCD_ShowString
	* @brief  在LCD屏幕上显示英文字符串
	* @param  usX: 起始X坐标
*           usY :起始Y坐标
*           pStr:要显示的英文字符串的首地址
*           usColor_Background :选择英文字符的背景色
*           usColor_Foreground :选择英文字符的前景色
*           font:字体选择
*             参数:ASCII_font_16 :16号字体
*                   ASCII_font_24 :24号字体 
	* @retval None      
*/
static void LCD_ShowString(uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,ASCII_font_t font)
{
	while (* pStr != '\0')
	{
		//自动换行
		if ((usX + font/2) > LCD_WIDTH)
		{
			usX = 0;
			usY += font;
		} 
		//自动换页
		if ((usY + font) > LCD_HEIGTH)
		{
			usX = 0;
			usY = 0;
		}
		//显示字符
		TFT_LCD.LCD_ShowChar(usX, usY, * pStr, usColor_Background, usColor_Foreground,font);
		//更新位置
		pStr ++;      
		usX += font/2;
	}
}

/*
	* @name   LCD_ShowCHN
	* @brief  在LCD屏幕上显示1个中文字符
	* @param  usX: 起始X坐标
*           usY :起始Y坐标
*           pStr:要显示的英文字符串的首地址
*           usColor_Background :选择英文字符的背景色
*           usColor_Foreground :选择英文字符的前景色
*           font:字体选择
*             参数:CHN_font_16 :16号字体
*                   CHN_font_24 :24号字体 
	* @retval None      
*/
static void LCD_ShowCHN(uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,CHN_font_t font)
{
	uint8_t ucTemp, ucPage, ucColumn;
	uint16_t usIndex; //字库中的汉字索引
	uint16_t CHN_Num; //字库中的汉字数量
	
	//检查输入参数是否合法
  assert_param(IS_ASCII_font(font));
	//判断字体 - 16号字体
	if(font == CHN_font_16)
	{
		//统计汉字数量
		CHN_Num = sizeof(FONT_CHN16) / sizeof(FONT_CHN16_t);
		//for循环查找汉字位置
		for(usIndex=0;usIndex<CHN_Num;usIndex++)
		{
			if((FONT_CHN16[usIndex].Index[0] == *pStr) && (FONT_CHN16[usIndex].Index[1] == *(pStr+1)))
			{
				//设置窗口,大小为16x16
				LCD_SetWindows(usX,usY,16,16);
				//逐行写入数据,共16行,每行16个像素点
				for(ucPage=0;ucPage<32;ucPage++)
				{
					//从ASCII字符集数组获取像素数据	
					//像素点数据为1时,写入字符颜色,为0时,写入背景颜色
					ucTemp = FONT_CHN16[usIndex].CHN_code[ucPage];
					for(ucColumn=0;ucColumn<8;ucColumn++)
					{
						if((ucTemp & BIT0) == BIT0)
							LCD_Write_DATA(usColor_Foreground);			
						else
							LCD_Write_DATA(usColor_Background);								
						ucTemp >>= 1;					
					}
				}
						
				break; //已找到并显示了汉字,退出循环
			}
		}	
	}
	
	//判断字体 - 24号字体
	if(font == CHN_font_24)
	{
		//统计汉字数量
		CHN_Num = sizeof(FONT_CHN24) / sizeof(FONT_CHN24_t);
		//for循环查找汉字位置
		for(usIndex=0;usIndex<CHN_Num;usIndex++)
		{
			if((FONT_CHN24[usIndex].Index[0] == *pStr) && (FONT_CHN24[usIndex].Index[1] == *(pStr+1)))
			{
				//设置窗口,大小为24x24
				LCD_SetWindows(usX,usY,24,24);
				//逐行写入数据,共24行,每行24个像素点
				for(ucPage=0;ucPage<72;ucPage++)
				{
					//从ASCII字符集数组获取像素数据	
					//像素点数据为1时,写入字符颜色,为0时,写入背景颜色
					ucTemp = FONT_CHN24[usIndex].CHN_code[ucPage];
					for(ucColumn=0;ucColumn<8;ucColumn++)
					{
						if((ucTemp & BIT0) == BIT0)
							LCD_Write_DATA(usColor_Foreground);			
						else
							LCD_Write_DATA(usColor_Background);								
						ucTemp >>= 1;					
					}
				}
						
				break; //已找到并显示了汉字,退出循环
			}
		}	
	}
}

/*
	* @name   LCD_ShowCHNandENGstring
	* @brief  在LCD屏幕上显示中英文字符串
	* @param  usX: 起始X坐标
*           usY :起始Y坐标
*           pStr:要显示的中英文字符串的首地址
*           usColor_Background :选择字符的背景色
*           usColor_Foreground :选择字符的前景色
*           font_CHN:  中文字体选择
*             参数:CHN_font_16 :16号字体
*                   CHN_font_24 :24号字体 
*           font_ASCII:ASCII码字体选择
*             参数:ASCII_font_16 :16号字体
*                   ASCII_font_24 :24号字体 
	* @retval None      
*/
static void LCD_ShowCHNandENGstring(uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,CHN_font_t font_CHN,ASCII_font_t font_ASCII)
{
	while (* pStr != '\0')
	{
		//中文字符
		if((* pStr) > 127)
		{
			//自动换行
			if ((usX + font_CHN) > LCD_WIDTH)
			{
				usX = 0;
				usY += font_CHN;
			} 
			//自动换页
			if ((usY + font_CHN) > LCD_HEIGTH)
			{
				usX = 0;
				usY = 0;
			}
			//显示中文字符
			TFT_LCD.LCD_ShowCHN(usX, usY, pStr, usColor_Background, usColor_Foreground,font_CHN);
			//更新位置
			pStr += 2;      
			usX += font_CHN;
		}
		//英文字符
		else
		{
			if((* pStr == '\r') | (* pStr == '\n'))
		  {
				//前面的字符为中文
				if((* (pStr-1)) > 127)
				{
					//换行
					usX = 0;
					usY += font_CHN;
				}
				//前面的字符为英文
				else
				{
					//换行
					usX = 0;
					usY += font_ASCII;	
				}							
			}
			else
			{
				//自动换行
				if ((usX + font_ASCII/2) > LCD_WIDTH)
				{
					usX = 0;
					usY += font_ASCII;
				} 
				//自动换页
				if ((usY + font_ASCII) > LCD_HEIGTH)
				{
					usX = 0;
					usY = 0;
				}
				//显示字符
				TFT_LCD.LCD_ShowChar(usX, usY, * pStr, usColor_Background, usColor_Foreground,font_ASCII);
				//更新位置     
				usX += font_ASCII/2;
			}
			//指向下一个字符
      pStr ++; 			
		}		
	}
}

/*
	* @name   LCD_ShowPicture
	* @brief  在LCD屏幕上显示图片
	* @param  usX    :起始X坐标
*           usY    :起始Y坐标
*           Pic_H  :图片水平分辨率
*           Pic_V  :图片垂直分辨率
*           Pic_num:图片序号

	* @retval None      
*/
static void LCD_ShowPicture(uint16_t usX, uint16_t usY,uint16_t Pic_H, uint16_t Pic_V,uint8_t Pic_num)
{
	uint32_t ulIndex;
	const uint8_t* pic = NULL;
	
	//设置窗口,大小为Pic_HxPic_V
	LCD_SetWindows(usX,usY,Pic_H,Pic_V);
	//获取图像数据首地址
	switch(Pic_num)
	{
		case 1: pic = gImage_Pic1; break;
		case 2: pic = gImage_Pic2; break;
		default:pic = gImage_Pic1;
	}

	//逐行写入图片数据
	for(ulIndex=0;ulIndex<Pic_H*Pic_V*2;ulIndex+=2)
	{
		LCD_Write_DATA((pic[ulIndex]<<8) | pic[ulIndex+1]);
	}
}	
/********************************************************
  End Of File
********************************************************/

路溪非溪
关注 关注
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
学习STM32的串口WiFi模块
m0_75244442的博客
09-13 406
总结: 通过本文的代码案例,我们了解了如何使用STM32与串口WiFi模块进行通信。我们学习了如何配置硬件和软件,并编写了相应的代码来实现WiFi模块的连接和发送数据。要学习STM32的串口WiFi模块内容,我们需要了解如何使用STM32的硬件串口功能和WiFi模块进行通信。在以上代码中,我们初始化了UART串口,通过串口与WiFi模块进行通信。代码实现: 首先,我们需要在代码中包含一些必要的库文件和头文件。接下来,我们将详细介绍如何连接和配置硬件,以及如何编写代码来实现串口WiFi模块的使用。
STM32F0实战 基于HAL库开发 (高显生著)示例代码
04-15
HAL(Hardware Abstraction Layer)库是ST为STM32系列微控制器提供的一种高级编程接口,它旨在简化MCU的软件开发过程,降低硬件驱动的复杂性,提高代码的可移植性和可读性。 本实践教程基于STM32F0,通过HAL库进行...
STM32 SDIO WiFi Marvell 8801 Wi-Fi模块:下一代嵌入式网络解决方案
最新发布
gitblog_00037的博客
04-22 724
STM32 SDIO WiFi Marvell 8801 Wi-Fi模块:下一代嵌入式网络解决方案 去发现同类优质开源项目:https://gitcode.com/ STM32 SDIO WiFi Marvell 8801 是一个强大的嵌入式Wi-Fi模块,专为在STM32微控制器平台上实现无线网络功能而设计。这个开源项目通过GitCode平台提供,让开发者能够轻松地集成Wi-Fi功能到他们的产品...
基于STM32SDIO接口与WiFi模块联合应用探索
weixin_66608063的博客
01-28 1258
在实际应用中,可以结合 SDIO 接口和 WiFi 模块,实现无线数据的存储和传输。例如,可以通过 WiFi 模块从远程服务器下载数据,并存储到 SD 卡中,或者将 SD 卡中的数据通过 WiFi 模块上传到远程服务器。在这种应用场景下,SDIO 接口用于实现大容量存储器访问(如SD卡),而 WiFi 模块用于实现无线数据传输。根据具体需求和应用场景,可以进一步优化和扩展这些功能,例如加入数据加密、传输协议优化等功能,以满足更复杂的应用需求。☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。
STM32MP157驱动开发——Linux WIFI驱动
weixin_45682654的博客
01-04 2914
Linux下的wifi驱动开发,主要内容为创建sdmmc3节点给wifi设备使用,然后使能厂商的wifi驱动。最后进行了简单的wifi功能测试。
stm32的wife驱动
weixin_42583683的博客
02-12 266
STM32WIFI驱动指的是在STM32芯片上驱动WIFI模块的软件驱动程序。这种驱动程序通常由STM32厂商提供,并与特定的WIFI模块配合使用。它通过STM32芯片上的串行接口,如UART或SPI,与WIFI模块通信,以实现WIFI通信功能。 ...
STM32——SDIO的学习(驱动SD卡)(理论篇)
qq_52479948的博客
05-23 1万+
多媒体卡)由西门子公司Siemens和SanDisk于1997年推出。由于它的封装技术较为先进,7针引脚,体积小、重量轻、非常符合移动存储的需要。MMC支持1bit模式,20MHz时钟,采用总线结构。MMC卡在SM卡基础上诞生替代了东芝开发的SM卡。不久的几年后,在MMC卡基础上研发的SD卡又替代了MMC卡,成为了几乎一切便携式数码产品的存储卡格式。SD卡结构SD卡是由松下电器东芝和SanDisk联合推出,1999年8月发布。SD卡的数据传送。
STM32驱动SDIO WIFI 介绍(十六) ---- 上位机UDP操作/代码
朝气蓬勃
01-10 4456
在介绍SDIO wifi Marvell8801/Marvell88w8801之前先附上模块链接:点击购买Marvell8801模块 // 点击购买Marvell8801开发板 Marvell自己实现驱动系列文章分为几篇介绍: SDIO wifi Marvell8801/Marvell88w8801 介绍(一) ---- 芯片介绍 SDIO wifi Marvell8801/Marvell88w8...
HAL库-STM32F407:CAN通信-收发实验-程序源码
05-04
使用STM32CubeMX生成HAL库工程文件。 MCU芯片为STM32F407VET6,实现CAN通信收发实验。 CAN通信波特率500k。 发送功能:每隔1s发送一帧CAN通信数据。 接收功能:将接收到的CAN通信的帧ID发送出来。
STM32F103单片机HAL库例程-SDIO接口SD卡FatFs移植与读写测试.rar
03-22
2、代码使用KEIL HAL库开发,当前在STM32F103运行,如果是STM32F103其他型号芯片,依然适用,请自行更改KEIL芯片型号以及FLASH容量即可。 3、软件下载时,请注意keil选择项是jlink还是stlink。 4、技术v:349014857;...
HALSTM32F407:Uart串口通信实验-程序源码
10-19
在本文中,我们将深入探讨如何使用STM32CubeMX生成HAL库工程,并在STM32F407VET6微控制器上实现Uart串口通信。STM32CubeMX是一款强大的配置工具,它简化了STM32微控制器的初始化过程,支持HAL(Hardware Abstraction...
stm32+8385wifi模块驱动
05-22
stm32裸机环境下,驱动SDIOwifi模块进行网络通信与控制,无任何问题。
STM32低成本WiFi播放电路设计详解
08-10
本设计基于已经发展成熟的WiFi 无线网络,充分利用WiFi 覆盖范围广、传输速度快、抗干扰能力强等优点。Android 具有开源性、封装性、性价比高等优点,基于Android 系统开发的客户端软件移植性强,通用性高。采用手机作为控制终端,便于操作。手机通过无线网络(WiFi)对音乐播放系统进行控制,可以实现方便、快捷、智能化的要求。
HALSTM32F407:LED灯实验
10-15
HAL库(Hardware Abstraction Layer,硬件抽象层)是STM32的一种高级API,它提供了一组统一的函数接口,简化了开发者对STM32外设的操作。 首先,我们需要下载并安装STM32CubeMX,这是一个图形化的配置工具,用于...
stm32中的SDIO
elderingezez的博客
01-25 2377
要使用 SD 卡制造商特定的 ACMD 命令如 ACMD6,需要在发送该命令之前发送 CMD55 命令,告知 SD 卡接下来的命令为特定应用命令。CMD55 命令只对紧接的第一个命令有效,SD 卡如果检测到 CMD55 之后的第一条命令为 ACMD 则执行其特定应用功能,如果检测发现不是 ACMD 命令,则执行标准命令。CMD12可以中断正在进行的数据通信,让卡返回到传输状态。• CMD:命令控制线,SDIO主机通过该线发送命令控制SD卡,如果命令要求SD卡提供应答,SD卡也是通过该线传输应答信息;
STM32SDIO-WIFI资料归纳
热门推荐
张子傲(C/C++)
07-02 1万+
资料一、 (1) Wifi卡的常用接口有: –CF 接口 –USB接口 –SDIO接口 –SPI接口 –PCMCIA接口 很多时候,同一个wifi卡同时支持多种接口,譬如marvell的8686的wifi卡,既支持spi接口,也支持sdio接口.   (2) SD卡与SDIO卡的异同 1.SD卡使用的是SD卡协议,而SDIO卡使用的是
STM32驱动SDIO wifi Marvell8801/Marvell88w8801 介绍(十四) ---- 上位机STA操作/代码/上位机操作连接open/wpa/wap2热点
朝气蓬勃
01-10 1563
在介绍SDIO wifi Marvell8801/Marvell88w8801之前先附上模块链接:点击购买Marvell8801模块 // 点击购买Marvell8801开发板 Marvell自己实现驱动系列文章分为几篇介绍: SDIO wifi Marvell8801/Marvell88w8801 介绍(一) ---- 芯片介绍 SDIO wifi Marvell8801/Marvell88w8...
自己实现SDIO wifi Marvell8801/Marvell88w8801驱动 介绍(一) ---- 芯片介绍
朝气蓬勃
01-10 8815
在介绍SDIO wifi Marvell8801/Marvell88w8801之前先附上模块链接:点击购买Marvell8801模块 // 点击购买Marvell8801开发板 Marvell自己实现驱动系列文章分为几篇介绍: SDIO wifi Marvell8801/Marvell88w8801 介绍(一) ---- 芯片介绍 SDIO wifi Marvell8801/Marvell88w8...
STM32/AT32系列WIFI SDIO卡通信初始流程配置
weixin_44516284的博客
11-27 2174
STM/AT32系列WIFI SDIO移植初始流程 1.SDIO GPIO配置 不同的硬件设计,所对应的GPIO也会存在不同。 此部分主要用于配置SDIO功能所对应的D0、D1、D2、D3、CMD、CLK。 另外,有些WIFI设计依赖于硬复位,复位脚所对应的PIN同样需要配置: 电平下拉->延时->电平上拉 2.SDIO外部时钟配置 SDIO通信模式包含至少POLLING和DMA两种方式。 如果需要采用DMA的话,则DMA设备时钟也应一并配置,至少SDIO设备为必须配置项。 另外,不同的芯片
零基础STM32开发实战指南:HAL单片机教程
资源摘要信息:"零基础入门STM32,基于HAL库开发! 单片机.zip" 本资源包是为想要入门STM32单片机开发的初学者量身打造的,内容涵盖了基于HAL库的STM32开发基础知识和实践操作。HAL库是ST公司推出的一种硬件抽象层库...
写文章

热门文章

  • C语言枚举详解 49072
  • PADS(一)简介、安装与基本使用 30952
  • STC-ISP使用指南 30385
  • TFT驱动ST7789使用总结 27898
  • 四级单词大全o-z 27171

分类专栏

  • 算法 1篇

最新评论

  • FREERTOS队列详解

    2301_77426322: 讲的太好了表情包表情包表情包表情包

  • 基于Gui Guider进行LVGL的页面绘制和移植

    savoureux: 哈哈谢谢,不用了,我找到1.4的包了

  • 如何一步一步地优化LVGL的flash占用

    A-code: 说明你代码上有用到,你得重新打开,只能关闭你没使用到的插件(控件)

  • 基于Gui Guider进行LVGL的页面绘制和移植

    飞翔的老宫本: 我有1.6

  • Linux下VSCode的安装和基本使用

    阿J~: 学起来,头秃的那种~

最新文章

  • Linux SPI 驱动实验
  • Linux I2C 驱动实验
  • Linux RTC 驱动实验
2024
10月 15篇
09月 23篇
08月 34篇
07月 23篇
06月 15篇
05月 3篇
04月 1篇
03月 21篇
02月 15篇
01月 11篇
2023年73篇
2022年147篇

目录

目录

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家深圳商场玻璃钢雕塑工厂潍坊市玻璃钢雕塑定制武汉定做玻璃钢卡通雕塑昭通玻璃钢雕塑定做杭州商场国庆美陈常州广场玻璃钢花盆盐城玻璃钢海豚雕塑价格玻璃钢艺术雕塑定做榆林玻璃钢雕塑厂家价格太湖石玻璃钢卡通雕塑设计朝阳园林玻璃钢雕塑厂家户外玻璃钢雕塑方法江苏创意玻璃钢雕塑生产厂家丹徒玻璃钢花盆花器安庆玻璃钢雕塑造价砂岩花盆玻璃钢雕塑江苏多彩玻璃钢雕塑优势陕西玻璃钢人物雕塑生产公司玻璃钢人物雕塑哪家实惠上海升美玻璃钢雕塑玻璃钢雕塑怕不怕雨济宁市玻璃钢人物雕塑辽宁大型玻璃钢雕塑厂太湖玻璃钢蔬菜雕塑伊川玻璃钢雕塑设计草莓玻璃钢卡通雕塑设计河南大型玻璃钢雕塑摆件玻璃钢铜雕塑制作宁德手糊法玻璃钢雕塑厂家多种玻璃钢雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化