基于HAL库的STM32F704的电阻式触摸屏的学习

  • 一、XPT2046介绍
  • 二、XPT2046底层代码的编写
    • 1.根据时序图编写读写程序
    • 2.XPT2046命令
  • 三、主要代码分析
    • 1.结构体以及初始化
    • 1.判断是否触摸
    • 2.校准
  • 四、实战

内容基于正点原子的代码分析

一、XPT2046介绍

XPT2046是一款4导线制触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置, 除此之外,还可以测量加在触摸屏上的压力。内部自带2.5V参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从0V到6V。XPT2046片内集成有一个温度传感器


红框内为XPT2046与MCU通讯用引脚(错了,NO BUSY;YES PENIRQ )

二、XPT2046底层代码的编写

1.根据时序图编写读写程序


时序图,课根据时序图,写出伪代码,再讲伪代码编辑为实际代码。关于延时部分参考XPT2046数据手册(请点进去后复制网址)。这里不再重复造轮子


/*
*************************************************************
*伪代码,函数功能:MCU发送数据给XPT2046,XPT2046返回数据给MCU
**************************************************************
*/
void xpt2046_write_byte(u8 write_data)
{for(i=0;i<8;i++){DCLK = 0;      if(write_data & 0x80)//S为1时有效TDIN = 1;//MOSIelse TDIN = 0;//1us       //MCU准备数据完成DCLK = 1;//1usrite_data <<= 1;}
}u16 xpt2046_read(u8 CMD)
{u16 xpt2046_read = 0;CS = 0;//片选DCLK  = 0;//拉低DIN = 0;xpt2046_write_byte(CMD);/*  过滤忙信号 */DCLK = 0;//延时       DCLK = 1;//延时for(i = 0 ; i<16;i++){DCLK = 0;    //XPT2046开始准备数据//1usDCLK = 1;  //XPT2046开始准备数据xpt2046_read<<=1;//空出最低位来保存读取到的数据 xpt2046_read |= DOUT;//MISO//1us}DCLK = 0; //完整周期CS = 1;      //取消选择return (xpt2046_read<<=4);
}
/*
*************************************************************
*正点原子的代码
**************************************************************
*/
//SPI写数据
//向触摸屏IC写入1byte数据
//num:要写入的数据
void TP_Write_Byte(u8 num)
{  u8 count=0;   for(count=0;count<8;count++)  {       if(num&0x80)TDIN=1;  else TDIN=0;   num<<=1;    TCLK=0; delay_us(1);TCLK=1;        //上升沿有效         }
}
//SPI读数据
//从触摸屏IC读取adc值
//CMD:指令
//返回值:读到的数据
u16 TP_Read_AD(u8 CMD)
{    u8 count=0;     u16 Num=0; TCLK=0;      //先拉低时钟      TDIN=0;   //拉低数据线TCS=0;      //选中触摸屏ICTP_Write_Byte(CMD);//发送命令字delay_us(6);//ADS7846的转换时间最长为6usTCLK=0;                 delay_us(1);           TCLK=1;     //给1个时钟,清除BUSYdelay_us(1);    TCLK=0;               for(count=0;count<16;count++)//读出16位数据,只有高12位有效 {                   Num<<=1;    TCLK=0;   //下降沿有效                delay_us(1);    TCLK=1;if(DOUT)Num++;          }      Num>>=4;     //只有高12位有效.TCS=1;      //释放片选   return(Num);
}

2.XPT2046命令

对应的两个命令:

u8 CMD_RDX=0XD0;//0XD0 为 11010000,读取X轴
u8 CMD_RDY=0X90;//0X90 为 10010000,读取Y轴

三、主要代码分析

1.结构体以及初始化

typedef struct
{u8 (*init)(void);          //初始化触摸屏控制器u8 (*scan)(u8);              //扫描触摸屏.0,屏幕扫描;1,物理坐标;   void (*adjust)(void);      //触摸屏校准 u16 x[CT_MAX_TOUCH];        //当前坐标u16 y[CT_MAX_TOUCH];      //电容屏有最多5组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用//x[4],y[4]存储第一次按下时的坐标. u8  sta;                 //笔的状态 //b7:按下1/松开0; //b6:0,没有按键按下;1,有按键按下. //b5:保留//b4~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
/触摸屏校准参数(电容屏不需要校准)//                                float xfac;                 float yfac;short xoff;short yoff;
//新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
//b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
//   1,横屏(适合左右为Y坐标,上下为X坐标的TP)
//b1~6:保留.
//b7:0,电阻屏
//   1,电容屏 u8 touchtype;
}_m_tp_dev;
/*
*****************************************************
结构体初始化
*****************************************************
*/
_m_tp_dev tp_dev=
{TP_Init,TP_Scan,TP_Adjust,0,0, 0,0,0,0,                0,0,
};

通过结构体方便控制底层,HAL库也是如此,值得学习,包含了函数指针、变量等。其中
TP_Init(),TP_Scan(),TP_Adjust()是重点掌握的函数。

1.判断是否触摸

//
//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 TP_Scan(u8 tp)
{              if(PEN==0)//有按键按下,整个面板相当于一个大的上拉电阻,应该是类似于一个按键的功能{if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//读取物理坐标else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//读取屏幕坐标{tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//LCDx=xfac*Px+xoff; 将结果转换为屏幕坐标tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff;//LCDy=yfac*Py+yoff;   需要校准} if((tp_dev.sta&TP_PRES_DOWN)==0)//之前没有被按下{       tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按键按下         #define TP_PRES_DOWN 0x80       触屏被按  下     #define TP_CATH_PRES 0x40  tp_dev.x[4]=tp_dev.x[0];//记录第一次按下时的坐标tp_dev.y[4]=tp_dev.y[0];                 }             }else{if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的{tp_dev.sta&=~(1<<7);//b1置0,标志标记按键松开 }else//之前就没有被按下{tp_dev.x[4]=0;tp_dev.y[4]=0;tp_dev.x[0]=0xffff;tp_dev.y[0]=0xffff;}     }return tp_dev.sta&TP_PRES_DOWN;//返回当前的触屏状态
}     

2.校准

//触摸屏校准代码
//得到四个校准参数
void TP_Adjust(void)
{                                u16 pos_temp[4][2];//坐标缓存值u8  cnt=0;  u16 d1,d2;u32 tem1,tem2;double fac;     u16 outtime=0;cnt=0;              POINT_COLOR=BLUE;BACK_COLOR =WHITE;LCD_Clear(WHITE);//清屏   POINT_COLOR=RED;//红色 LCD_Clear(WHITE);//清屏       POINT_COLOR=BLACK;LCD_ShowString(40,40,160,100,16,(u8*)TP_REMIND_MSG_TBL);//显示提示信息TP_Drow_Touch_Point(20,20,RED);//画点1 tp_dev.sta=0;//消除触发信号 tp_dev.xfac=0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误     while(1)//如果连续10秒钟没有按下,则自动退出{tp_dev.scan(1);//扫描物理坐标if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按键按下了一次(此时按键松开了.){  outtime=0;     tp_dev.sta&=~(1<<6);//标记按键已经被处理过了.pos_temp[cnt][0]=tp_dev.x[0];pos_temp[cnt][1]=tp_dev.y[0];cnt++;     switch(cnt){             case 1:                       TP_Drow_Touch_Point(20,20,WHITE);              //清除点1 TP_Drow_Touch_Point(lcddev.width-20,20,RED); //画点2break;case 2:TP_Drow_Touch_Point(lcddev.width-20,20,WHITE);    //清除点2TP_Drow_Touch_Point(20,lcddev.height-20,RED); //画点3break;case 3:TP_Drow_Touch_Point(20,lcddev.height-20,WHITE);           //清除点3TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,RED);    //画点4break;case 4:   //全部四个点已经得到//对边相等tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,2的距离tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到3,4的距离fac=(float)d1/d2;if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE);   //清除点4TP_Drow_Touch_Point(20,20,RED);                               //画点1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据   continue;}tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,3的距离tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,4的距离fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4TP_Drow_Touch_Point(20,20,RED);                               //画点1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据   continue;}//正确了//对角线相等tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,4的距离tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,3的距离fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4TP_Drow_Touch_Point(20,20,RED);                               //画点1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据   continue;}//正确了//计算结果tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac      tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xofftp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfactp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff  if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//触屏和预设的相反了.{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE);   //清除点4TP_Drow_Touch_Point(20,20,RED);                               //画点1LCD_ShowString(40,26,lcddev.width,lcddev.height,16,"TP Need readjust!");tp_dev.touchtype=!tp_dev.touchtype;//修改触屏类型.if(tp_dev.touchtype)//X,Y方向与屏幕相反{CMD_RDX=0X90;CMD_RDY=0XD0;    }else                 //X,Y方向与屏幕相同{CMD_RDX=0XD0;CMD_RDY=0X90;     }              continue;}      POINT_COLOR=BLUE;LCD_Clear(WHITE);//清屏LCD_ShowString(35,110,lcddev.width,lcddev.height,16,"Touch Screen Adjust OK!");//校正完成delay_ms(1000);TP_Save_Adjdata();  LCD_Clear(WHITE);//清屏   return;//校正完成               }}delay_ms(10);outtime++;if(outtime>1000){TP_Get_Adjdata();break;} }
}

算了,就这样吧,不想写了

四、实战

CUBUMX相关配置

/*
*************************************************************************************
简易画板
*************************************************************************************
*/
static u8 Dft_Scan_Dir = 0;
u16 Ssd_Hor_Resolution  =320;      //LCD水平分辨率
u16 Ssd_Ver_Resolution  =240;      //LCD垂直分辨率void drow_block_colar(void)
{   LCD_Fill(0,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*1/6,Ssd_Ver_Resolution,RED);LCD_Fill(Ssd_Hor_Resolution*1/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*2/6,Ssd_Ver_Resolution,YELLOW);LCD_Fill(Ssd_Hor_Resolution*2/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*3/6,Ssd_Ver_Resolution,BLUE);LCD_Fill(Ssd_Hor_Resolution*3/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*4/6,Ssd_Ver_Resolution,GREEN);LCD_Fill(Ssd_Hor_Resolution*4/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*5/6,Ssd_Ver_Resolution,BLACK);LCD_ShowString(Ssd_Hor_Resolution*5/6+1,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution,16,16,"ERASER");//显示清屏区域
}
//清空屏幕并在右上角显示"RST"
void Load_Drow_Dialog(void)
{LCD_Clear(WHITE);//清屏   POINT_COLOR=BLUE;//设置字体为蓝色 LCD_ShowString(lcddev.width-24,0,200,16,16,"RST");//显示清屏区域POINT_COLOR=RED;//设置画笔蓝色 drow_block_colar();
}
void rtp_test(void)
{u8 key;u8 i=0;      while(1){key=Key_Scan(0);tp_dev.scan(0);          if(tp_dev.sta&TP_PRES_DOWN)            //触摸屏被按下{   if(tp_dev.x[0]<lcddev.width&&tp_dev.y[0]<lcddev.height){  if(tp_dev.x[0]>(lcddev.width-24)&&tp_dev.y[0]<16)Load_Drow_Dialog();//清除else if(tp_dev.y[0]>Ssd_Ver_Resolution-40&&tp_dev.y[0]<Ssd_Ver_Resolution){ if(tp_dev.x[0]>0&&tp_dev.x[0]<=Ssd_Hor_Resolution*1/6)POINT_COLOR = RED;else if(tp_dev.x[0]>Ssd_Hor_Resolution*1/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*2/6)POINT_COLOR = YELLOW;else if(tp_dev.x[0]>Ssd_Hor_Resolution*2/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*3/6)POINT_COLOR = BLUE;else if(tp_dev.x[0]>Ssd_Hor_Resolution*3/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*4/6)POINT_COLOR = GREEN;else if(tp_dev.x[0]>Ssd_Hor_Resolution*4/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*5/6)POINT_COLOR = BLACK;else if(tp_dev.x[0]>Ssd_Hor_Resolution*5/6&&tp_dev.x[0]<=Ssd_Hor_Resolution)POINT_COLOR = BACK_COLOR;   }else{  if(POINT_COLOR == BACK_COLOR)LCD_Fill(tp_dev.x[0],tp_dev.y[0],tp_dev.x[0]+5,tp_dev.y[0]+5,POINT_COLOR);elseTP_Draw_Big_Point(tp_dev.x[0],tp_dev.y[0],POINT_COLOR);      //画图}                   }}else delay_ms(10);    //没有按键按下的时候         if(key==KEY0_PRES)    //KEY0按下,则执行校准程序{LCD_Clear(WHITE);  //清屏TP_Adjust();        //屏幕校准 TP_Save_Adjdata();    Load_Drow_Dialog();}i++;if(i%20==0)PFout(9)=!PFout(9);}
}
/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */u8 lcd_id[12];              //存放LCD ID字符串u8 datatemp[SIZE];u8 KEY;u8 test[256];u8 i;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_FSMC_Init();MX_TIM6_Init();/* USER CODE BEGIN 2 */delay_init(168);HAL_TIM_Base_Start_IT(&htim6);LCD_Init();                     //初始化LCD FSMC接口AT24CXX_Init();                  //初始化IICif(Dft_Scan_Dir){Ssd_Hor_Resolution = 320;Ssd_Ver_Resolution = 240;}      else{Ssd_Hor_Resolution = 240;Ssd_Ver_Resolution = 320;       }LCD_Display_Dir(Dft_Scan_Dir);tp_dev.init();                   //触摸屏初始化 drow_block_colar();if(tp_dev.touchtype!=0XFF){LCD_ShowString(0,0,200,16,16,"Press KEY0 to Adjust");//电阻屏才显示}delay_ms(1500);Load_Drow_Dialog();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */rtp_test();  }/* USER CODE END 3 */
}

效果图:

基于HAL库的STM32F704的电阻式触摸屏的学习相关推荐

  1. STM32机器人控制开发教程No.3 使用遥控控制电机/舵机(基于HAL库)

    01 前言 本教程使用的机器人控制板拥有4个带编码器的电机接口,4个舵机接口,串口通信接口.SWD下载调试接口.航模遥控接口.USB5V输出接口以及方便与树莓派直接连接的40PIN接口等,板载资源丰富 ...

  2. STM32单片机基于HAL库开发HC-SR04 超声波测距模块(终极版)

    参考: 某宝HC-SR04 超声波测距模块商品详情页 STM32L051C8T6 HAL库 输入捕获进行超声波测距 案例 STM32CubeMX学习笔记3--TIM2输入捕获(SR-04测距) cub ...

  3. STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)

    目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...

  4. STM32CubeMX基于HAL库实现简单串口通信

    STM32CubeMX基于HAL库实现简单串口通信 板子:STM32F103C8 编译工具:Keil+STM32CubeMX 烧录工具:FlyMcu 串口调试程序:XCOM 实验要求: 1)设置波特率 ...

  5. STM32外挂FLASH模拟U盘(基于HAL库)

    STM32外挂FLASH模拟U盘(基于HAL库) 1.背景 1.1这篇文章能给你带来什么 1.2根据你要解决的问题,精确快速跳转到相应位置 1.3我在做完这个后还有不明白的地方,希望能有大触解答困惑 ...

  6. STM32软件学习笔记(一)基于HAL库的STM32F429单片机串口打印程序

    |版权声明:本文为博主原创文章,转载请注明出处.https://blog.csdn.net/NeverImagine_/article/details/95517664   目前ST官方有提供两种库文 ...

  7. STM32F4 HAL库开发 -- 新建基于 HAL 库的工程模板

    一.STM32CubeF4 固件包下载 下载:STM32CubeF4 MCU Firmware Package 二.新建基于 HAL 库的工程模板 1.新建文件夹 建立一个文件夹为 Template. ...

  8. STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数

    STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数 一.步进电机闭环驱动器 二.CubeMx配置 1.Clock Configuration 2.脉冲端 定时器配置 3. ...

  9. STM32F103VET6基于HAL库和标准库下DAC 心形波形输出

    STM32F103VET6基于HAL库和标准库下DAC 心形波形输出

  10. STM32F407学习笔记(1)基于hal库,CubeMX以及keil5的基本使用。

    前言:自己是大二的一名通信工程专业的小菜鸡,在实验室培训了四次之后,我们目前使用是stm32f407zgt6这块单片机.在为明年的电赛国赛准备着.实验室目前使用的工具是cubemax和keil这两款软 ...

最新文章

  1. 【控制】《复杂运动体系统的分布式协同控制与优化》-方浩老师-第11章-意图场模型干预下的人机共享控制
  2. java 属性访问修饰符_Java中的访问修饰符(二十七)
  3. 数据挖掘-分类与预测-神经网络算法
  4. 读书笔记-你不知道的JavaScript(上)
  5. 谁偷偷删了你的微信?别慌!一篇Python学习教程帮你都揪出来
  6. Alexa交叉编译(avs-device-sdk)
  7. 基于51单片机的红外接收
  8. Java8新特性 方法引用(二)
  9. macOS Monterey 12.2 (21D49) 正式版 ISO、IPSW、PKG 下载
  10. 大脑信息编码_编码人大脑的5大小吃
  11. source and destination ports as seperate columns in wireshark
  12. python判断word页码
  13. 一名理想主义的程序员
  14. 【UOS】如何用Ventoy工具制作UOS启动盘操作
  15. Mac电脑安装其他系统
  16. PDF如何设置注释字体大小
  17. 态度篇·与其混不如滚
  18. 飞凌imx6dl lvds闪屏问题记录
  19. 小米路由器提示网络连接问题
  20. 数据库原理及应用学习笔记

热门文章

  1. 学计算机要选什么科,新高考省份想学计算机专业怎么选科
  2. android测试基站,Android iPhone 手机查看基站信息
  3. android 根据基站测距,无线测距基站
  4. 超燃动态可视化条形图源码及效果图_HTML5大数据可视化效果(一)彩虹爆炸图...
  5. 机场精细化管理_王晓鸿:BIM技术在机场工程精细化管理的应用
  6. 登录注册判断+Mysql
  7. ❤️1000道《计算机基础知识》汇总上----(建议收藏)❤️
  8. 服务器输出字符流数据到浏览器 使用println浏览器无显示
  9. 五个脑叶的功能_人类大脑的生理构造及功能分区
  10. 程序员是如何下载的视频?