STM32基础入门学习笔记:开发板 电路原理与驱动编程

news2024/9/25 7:16:16

文章目录:

一:触摸按键

1.触摸按键驱动程序(点击)

touch_key.h 

touch_key.c

main.c

2.按键双击和长按程序

touch_key.h

touch_key.c

main.c

3.触摸按键滑动程序

main.c

二:数码管显示

1.数码管RTC时钟LED显示程序

 main.c

2.TM1640驱动程序

TM1640.h

TM1640.c

3.旋转编码器基于数码管的显示程序

main.c

encoder.h

encoder.c 

4.I2C总线(lm75a器件驱动)数码管显示温度值

i2c.h

i2c.c

lm75a.h

lm75a.c

main.c

三:OLED屏显示(采用L2C通信 体积小)

1.温度值OLED屏汉字、数字、字母显示程序

oled0561.h

oled0561.c

main.c

ASCII_8x16.h        ASCII码

2.OLED屏幕汉字、图片显示程序

oled0561.h

oled0561.c

main.c

CHS_16x16.h        汉字

PIC1.h        图片

四:继电器控制(按键控制继电器程序)

relay.h

relay.c

main.c

五:步进电机控制

1.按键控制步进电机程序 

step_motor.h

step_motor.c

main.c

2.步进电机步数控制程序(精密控制)

step_motor.h

step_motor.c

main.c

六:RS232串口

1.RS232串口通信测试程序

touch_key.h

touch_key.c

usart.h

usart.c

 main.c

2.RS485串口通信测试程序

usart.h

rs485.h

rs485.c

main.c

七:CAN总线  

1.理论原理分析 

1.1 CAN总线的发送 

1.2 CAN总线的接收

2.CAN通信测试程序 

can.h

can.c

main.c

八:ADC模数转换器

1.光敏电阻ADC读取程序(单个通道)

adc.h

adc.c

main.c

2.光敏和电位器ADC读取程序(两个通道)

adc.h

adc.c

main.c

3.模拟摇杆的ADC读取程序

adc.h

adc.c

JoyStick.h

JoyStick.c

main.c

九:MP3播放

1.MP3播放测试程序

usart.h

usart.c

MY1690.h

MY1690.c

main.c

2.MP3语音播报程序

rtc.h

rtc.c

main.c

十:SPI总线

1.CH376文件管理控制芯片 

2.U盘插拔测试程序

spi.h

spi.c

ch376.h

ch376.c

ch376inc.h

filesys.h

filesys.c

main.c

3.U盘读写文件程序

filesys.h

filesys.c

main.c


一:触摸按键

把继电器相关的两个跳线(J1 J2)断开:拔出来,单独插在J1 J2单独引脚上面,使得短接的跳线断开


原因:继电器相关的跳线断开,在上电时会输出低电平,如果没有初始化继电器就会吸合会消耗额外的功耗

新建文件夹

Hardware文件夹——>TOUCH_KEY文件夹——>touch_key.c touch_key.h

1.触摸按键驱动程序(点击)

点A键 LED1亮;点B键LED2亮;点C键LED1 LED2同时熄灭;点D键LED1 LED2同时点亮 

touch_key.h 

#ifndef __TOUCH_KEY_H
#define __TOUCH_KEY_H	 
#include "sys.h"

#define TOUCH_KEYPORT	GPIOA	//定义IO接口组
#define TOUCH_KEY_A		GPIO_Pin_0	//定义IO接口
#define TOUCH_KEY_B		GPIO_Pin_1	//定义IO接口
#define TOUCH_KEY_C		GPIO_Pin_2	//定义IO接口
#define TOUCH_KEY_D		GPIO_Pin_3	//定义IO接口


void TOUCH_KEY_Init(void);//初始化

		 				    
#endif

touch_key.c

#include "touch_key.h"

void TOUCH_KEY_Init(void){ //微动开关的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    GPIO_InitStructure.GPIO_Pin = TOUCH_KEY_A | TOUCH_KEY_B | TOUCH_KEY_C | TOUCH_KEY_D; //选择端口                       
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(TOUCH_KEYPORT,&GPIO_InitStructure);			
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"

#include "touch_key.h"

int main (void){//主程序
	RCC_Configuration(); //系统时钟初始化 
	LED_Init();//LED初始化

	TOUCH_KEY_Init();//按键初始化

	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //读触摸按键的电平
			GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制	
		}
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){ //读触摸按键的电平
			GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制	
		}
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){ //读触摸按键的电平
			GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制	
		}
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){ //读触摸按键的电平
			GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(1));//LED控制	
		}
	}
}

2.按键双击和长按程序

长按A键LED1点亮;双击A键时LED 2点亮;单击A键时LED1 LED 2同时熄灭 

 

单击

双击:就是两次单机,一次单击之后还有按键按下(计时器单机之后还有一个低电平)

长按:判断中间的按下时长是否超过3s


可以设置D2的时间长度:改变双击速度

touch_key.h

#ifndef __TOUCH_KEY_H
#define __TOUCH_KEY_H	 
#include "sys.h"

#define TOUCH_KEYPORT	GPIOA	//定义IO接口组
#define TOUCH_KEY_A		GPIO_Pin_0	//定义IO接口
#define TOUCH_KEY_B		GPIO_Pin_1	//定义IO接口
#define TOUCH_KEY_C		GPIO_Pin_2	//定义IO接口
#define TOUCH_KEY_D		GPIO_Pin_3	//定义IO接口


void TOUCH_KEY_Init(void);//初始化

		 				    
#endif

touch_key.c

#include "touch_key.h"

void TOUCH_KEY_Init(void){ //微动开关的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    GPIO_InitStructure.GPIO_Pin = TOUCH_KEY_A | TOUCH_KEY_B | TOUCH_KEY_C | TOUCH_KEY_D; //选择端口                       
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(TOUCH_KEYPORT,&GPIO_InitStructure);			
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "touch_key.h"

#define KEYA_SPEED1	100	  //长按的时间长度(单位10mS)
#define KEYA_SPEED2	10	  //双击的时间长度(单位20mS)


int main (void){//主程序
	u8 a=0,b,c=0;
	RCC_Configuration(); //系统时钟初始化 
	LED_Init();//LED初始化
	TOUCH_KEY_Init();//按键初始化
	while(1){

		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
				}else{ //单击处理
					for(b=0;b<KEYA_SPEED2;b++){//检测双击
						delay_ms(20);
						if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
							a=1;
							//双击后执行的程序放到此处
							GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制

							while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
						}
					}
					if(a==0){ //判断单击
						//单击后执行的程序放到此处
						GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
						
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束

	}
}

3.触摸按键滑动程序

在超级终端上面显示:A被单击;A被双击;A被长按....B键C键D键同理;ABCD右滑左滑

中间的间隔

按下之后,又按下了另外一个键:按下松开的是拿些按键

main.c

 
#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "touch_key.h"
#include "usart.h"
 
#define KEYA_SPEED1	100	  //长按的时间长度(单位10mS)
#define KEYA_SPEED2	10	  //双击的时间长度(单位20mS)
 
 
 
int main (void){//主程序
	u16 k=1000;	//用于滑动加减计数
	u8 a=0,b,c=0;
	u8 s=0; //刚刚结束滑动标志
	RCC_Configuration(); //系统时钟初始化 

	USART1_Init(115200); //串口初始化,参数中写波特率

	LED_Init();//LED初始化
	TOUCH_KEY_Init();//按键初始化
	while(1){
//A
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("A键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
				}else{ //单击处理
                    //判断是点击还是滑动
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){
						k++; //用于显示的计数值
						printf("A键右滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("A键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("A键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
//B
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("B键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B));
				}else{ //单击处理
                    //判断C键是不是被触发
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){
						k++;
						printf("B键右滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
                    //判断A键是不是被触发
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){
						k--;
						printf("B键左滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("B键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("B键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
//C
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("C键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C));
				}else{ //单击处理
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){
						k++;
						printf("C键右滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){
						k--;
						printf("C键左滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("C键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C));
							}
						}
						if(a==0){ //判断单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("C键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
//D
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){ //检测按键是否按下
			delay_ms(20); //延时去抖动
			if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){//判断长短键
				while((!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D))&&c<KEYA_SPEED1){ //循环判断长按,到时跳转
					c++;delay_ms(10); //长按判断的计时
				}
				if(c>=KEYA_SPEED1){ //长键处理
					//长按后执行的程序放到此处
					GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1));//LED控制
					printf("D键长按 \r\n");
					while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D));
				}else{ //单击处理
					if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){
						k--;
						printf("D键左滑 %d \r\n",k); 
						a=1;s=1; //a是单双击判断标志,s是刚刚结束滑动标志
					}
					if(a==0){
						for(b=0;b<KEYA_SPEED2;b++){//检测双击
							delay_ms(20);
							if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){
								a=1;
								//双击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED2,(BitAction)(1));//LED控制
								printf("D键双击 \r\n");
								while(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D));
							}
						}
						if(a==0){ //判断单击,避免滑动最后松开被认为是单击
							if(s==1){ //判断是不是刚执行完滑动操作
								s=0; //如果是则本次不执行单击处理(因为是滑动的放开操作)
							}else{	 //如果不是,则正常执行单击处理
								//单击后执行的程序放到此处
								GPIO_WriteBit(LEDPORT,LED1|LED2,(BitAction)(0));//LED控制
								printf("D键单击 \r\n");
							}
						}
					}
				}
				a=0;c=0; //参数清0
			}
		} //按键判断在此结束
 
	}
}

二:数码管显示

对跳线进行设置

第一步:看数码管上方的跳线帽,有两个跳线端子,确保跳线帽端子是插入的
       原因:单片机的IO端口才能和数码管的跳线端子进行连通


第二步:看摇杆上方的can总线(它的端口与数码管复用)
        使用的时候需要把复用的can总线断开,插入到A11 A12


第三步:看核心板右侧的跳线帽
        断开LM4871插入到GND(开发板上面的扬声器断开,因为扬声器会发出杂音)

1.数码管RTC时钟LED显示程序

 新建文件夹

Hardware文件夹——>TM1640文件夹——>TM1640.c TM1640.h

显示时间 

 main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "rtc.h"
#include "TM1640.h"



int main (void){//主程序
	u8 c=0x01;
	RCC_Configuration(); //系统时钟初始化 
	RTC_Config();  //RTC初始化
	TM1640_Init(); //TM1640初始化
	while(1){
		if(RTC_Get()==0){ //读出RTC时间
			TM1640_display(0,rday/10);	//天
            //显示固定数 640_display(0,1);
			TM1640_display(1,rday%10+10);
			TM1640_display(2,rhour/10); //时
			TM1640_display(3,rhour%10+10);
			TM1640_display(4,rmin/10);	//分
			TM1640_display(5,rmin%10+10);
			TM1640_display(6,rsec/10); //秒
			TM1640_display(7,rsec%10);
            //关闭显示 TM1640_display(7,20);

			TM1640_led(c); //与TM1640连接的8个LED全亮    也可用0000 0001=0x01表示,分别对应
			c<<=1; //数据左移 流水灯
			if(c==0x00)c=0x01; //8个灯显示完后重新开始
			delay_ms(125); //延时
		}
	}
}

2.TM1640驱动程序

数码管如何点亮、如何调节数码管的亮度,如何在数码管的某个位置显示某个数字

TM1640.h

#ifndef __TM1640_H
#define __TM1640_H	 
#include "sys.h"

#define TM1640_GPIOPORT	GPIOA	//定义IO接口
#define TM1640_DIN	GPIO_Pin_12	//定义IO接口
#define TM1640_SCLK	GPIO_Pin_11	//定义IO接口

#define TM1640_LEDPORT	0xC8	//定义IO接口


void TM1640_Init(void);//初始化
void TM1640_led(u8 date);//芯片所连接的LED驱动函数
void TM1640_display(u8 address,u8 date);//显示函数
void TM1640_display_add(u8 address,u8 date);//显示函数,且自动+1

		 				    
#endif

TM1640.c

#include "TM1640.h"
#include "delay.h"

#define DEL  1   //宏定义 通信速率(默认为1,如不能通信可加大数值)

//地址模式的设置
//#define TM1640MEDO_ADD  0x40   //宏定义	自动加一模式
#define TM1640MEDO_ADD  0x44   //宏定义 固定地址模式(推荐)

//显示亮度的设置
//#define TM1640MEDO_DISPLAY  0x88   //宏定义 亮度  最小
//#define TM1640MEDO_DISPLAY  0x89   //宏定义 亮度
//#define TM1640MEDO_DISPLAY  0x8a   //宏定义 亮度
//#define TM1640MEDO_DISPLAY  0x8b   //宏定义 亮度
#define TM1640MEDO_DISPLAY  0x8c   //宏定义 亮度(推荐)
//#define TM1640MEDO_DISPLAY  0x8d   //宏定义 亮度
//#define TM1640MEDO_DISPLAY  0x8f   //宏定义 亮度 最大

//关闭亮度,关闭数码管显示
#define TM1640MEDO_DISPLAY_OFF  0x80   //宏定义 亮度 关



void TM1640_start(){ //通信时序 启始(基础GPIO操作)(低层)
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1	
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1	
	delay_us(DEL);
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0	
	delay_us(DEL);
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0	
	delay_us(DEL);
}
void TM1640_stop(){ //通信时序 结束(基础GPIO操作)(低层)
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0	
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1	
	delay_us(DEL);
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1	
	delay_us(DEL);
}
void TM1640_write(u8 date){	//写数据(低层)
	u8 i;
	u8 aa;
	aa=date;
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0	
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0	
	for(i=0;i<8;i++){
		GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0	
		delay_us(DEL);

		if(aa&0x01){
			GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1	
			delay_us(DEL);
		}else{
			GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0	
			delay_us(DEL);
		}
		GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1	
		delay_us(DEL);
		aa=aa>>1;
   }
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(0)); //接口输出0	
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(0)); //接口输出0	
}



//TM1640接口初始化
void TM1640_Init(void){ 
    //对通信线上的两个IO端口进行初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
    GPIO_InitStructure.GPIO_Pin = TM1640_DIN | TM1640_SCLK; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(TM1640_GPIOPORT, &GPIO_InitStructure);

	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_DIN,(BitAction)(1)); //接口输出高电平1	
	GPIO_WriteBit(TM1640_GPIOPORT,TM1640_SCLK,(BitAction)(1)); //接口输出高电平1

    //设置芯片的地址模式,设置芯片的显示亮度	
	TM1640_start();
	TM1640_write(TM1640MEDO_ADD); //设置数据,0x40,0x44分别对应地址自动加一和固定地址模式
	TM1640_stop();
	TM1640_start();
	TM1640_write(TM1640MEDO_DISPLAY); //控制显示,开显示,0x88,  0x89,  0x8a,  0x8b,  0x8c,  0x8d,  0x8e,  0x8f分别对应脉冲宽度为:
					 				  //------------------1/16,  2/16,  4/16,  10/16, 11/16, 12/16, 13/16, 14/16	 //0x80关显示
	TM1640_stop();	
				
}
void TM1640_led(u8 date){ //固定地址模式的显示输出8个LED控制
   TM1640_start();
   TM1640_write(TM1640_LEDPORT);	        //传显示数据对应的地址
   TM1640_write(date);	//传1BYTE显示数据
   TM1640_stop();
}
void TM1640_display(u8 address,u8 date){ //固定地址模式的显示输出
 	const u8 buff[21]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef,0x00};//数字0~9及0~9加点显示段码表
    //---------------   0    1    2    3    4    5    6    7    8    9    0.   1.   2.   3.   4.   5.   6.   7.   8.   9.   无   
   TM1640_start();
   TM1640_write(0xC0+address);	         //传显示数据对应的地址
   TM1640_write(buff[date]);				 //传1BYTE显示数据
   TM1640_stop();
}
void TM1640_display_add(u8 address,u8 date){	//地址自动加一模式的显示输出 
	u8 i;
 	const u8 buff[21]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef,0x00};//数字0~9及0~9加点显示段码表
    //---------------   0    1    2    3    4    5    6    7    8    9    0.   1.   2.   3.   4.   5.   6.   7.   8.   9.   无   
  TM1640_start();
   TM1640_write(0xC0+address);	         //设置起始地址
   for(i=0;i<16;i++){
      TM1640_write(buff[date]); 
   }
   TM1640_stop(); 
}

3.旋转编码器基于数码管的显示程序

对开发板上面的跳线进行设置

第一步:找到数码管下面,标注为“选择编码器”,确保(数据线连接L D R)处在短接状态

第二步:在触摸按键的左边,标志位模拟摇杆,把(PA6 PA7 P82)断开跳线帽取下,插入到(D Y X),使跳线处在断开状态



逆时针旋转:-1
顺时针旋转:+1
向下按:清0

 

 新建文件夹

Hardware文件夹——>ENCODER文件夹——>encoder.c encoder.h

如何判断编码器的左右旋转

main.c

#include "encoder.h"


int main(void){
    u8 a=0,b=0,c=0x01;

    ENCODER_Init();    //编码器初始化

    TM1640 Init();     //TM1640初始化
        TM1640_dispiay(o,a/10) ; //显示数值
        TM1640_display (1,a%10);
        TM1640_display (2,20);
        TM1640_display (3,20);
        TM1640_display (4,20 );
        TM1640_display (5,20);
        TM1640_display (6,20);
        TM1640_display (7,20);


    while(1){
        b=ENCODER_READ();    //状态读取旋转编码器的值

        //让a的值介于1~99
            if(b==1){a++;if(a>99) a=0;        //向右顺时针旋转
            if(b==2){if(a==0) a=100;a--;}     //向左逆时针旋转
        if(b==3) a=0;                         //按下    a=0表示旋钮没有任何操作
        if(b!=0){//有旋转纽的操作
            TM1640_display(o,a/10); //显示数值
            TM1640_display (1,a%10);
        }

        TM1640_led(c);          //与TM1640连接的8个LED全亮
        C<<=1;                  //数据左移流水灯
        if(c==ox00) c=ox01;     //8个灯显示完后重新开始
        delay ms(150);          //延时
   }
}

encoder.h

#ifndef_ENCODER_H
#define_ENCODER_H

#include "sys.h"
#include "delay.h"

#define ENCODER_PORT_AGPIOA             //定义Io接口组
#define ENCODER_LG    PIo_Pin_6         //定义Io接口
#define ENCODER_DG    PIo_Pin_7         //定义Io接口

#define ENCODER_PORT_B    GPIOB         //定义Io接口组
#define ENCODER_R         GPIO_Pin_2    //定义Io接口


void ENCODER_Init (void);       //编码器初始化
u8 ENCODER_READ (void);         //编码器状态数值读取函数

#endif

encoder.c 

#include "encoder.h"

u8 KUP;    //旋钮死锁标志(1为死锁)
u16 cou;   //开始状态的,通用循环计数器的变量


//接口初始化    主要设置端口
void ENCODER_Init (void){
    GPIO_InitTypeDef    GPIO_InitStructure;              //定义GPIO的初始化枚举结构
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC);
    GPIO_InitStructure.GPIO_Pin = ENCODER_L | ENCODER_D; //旋转端口号
    GPIo_Initstructure.GPIo_Mode = GPIO_Mode_IPU;        //选择Io接口工作方式  //上拉电阻        
    GPIo_Init(ENCODER_PORT_A, &GPIO_Initstructure);

    GPIO Initstructure.GPIO_Pin = ENCODER_R;             //选择端口号
    GPIo_Initstructure.GPIo_Mode = GPro_Mode_IPU;        //选择Io接口工作方式//上拉电阻    
    GPIo_Init(ENCODER_PORT_B,&GPIO_Initstructure);
}



//编码器状态读取(第二种)
//第一种:判断K2 K3那个先触发(判断2个IO端口状态)    第二种:判断K2为低电平时K3的状态(判断1个IO端口状态)
u8 ENCODER_READ(void){
    u8 a;	//存放的值
    u8 kt;
    a=0;

    if(GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)) KUP=0;	     //判断旋钮是否解除锁死;K2为高电平了
    //判断K2是否是低电平
    if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L) && KUP==0){  //判断是否旋转旋钮,同时看是否锁死
	    delay_us(100);
        //判断K3的状态
	    kt=GPIO_ReadInputDataBit(ENCODER_PORT_B,ENCODER_R) ;	     //把旋钮另一端电平状态记录
	    delay_ms(3);	//延时,去除机械抖动
	
        //读取K2所连的IO端口    是,代表按键有效
        if(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L)){	     //去抖
	        if(kt==0){	//用另一端判断左或者右旋转    K3低电平
		        a=1;	//右转
            }else{      //K3高电平
		        a=2;	//左转
	        }

	       cou=0;		//初始锁死判断计数器

           //判断K2按键是否放开——高电平    一直循环    20微妙*60000次=1.2s
           //超过1.2s,就是卡死状态,那么跳出循环执行其他程序,不会卡死在while循环中
	       while(!GPIO_ReadInputDataBit(ENCODER_PORT_A,ENCODER_L) && cou<60000){		//等待放开
		        cou++;
		        KUP=1;
		        deay_us(20);
	       }
        }
    }

    //判断按键按下
    if(!GPIo_ReadInputDataBit(ENCODER_PORT_A,ENCoDER_D) a KUP==0){//判断旋钮是否按下
        delay_ms (20);
        if (!GPIo_ReadInputDataBit(ENCODER_PORT_A,ENCODER_D)){    //去抖动
            a=3;                    //在按键按下时加上按键的状态值
            //while (ENCODER_D==0) ;等等旋钮放开
           }
    }
    return a;
}

如何处理编码器卡死的问题、扫描延时造成的读取错误

如何处理编码器卡死的问题
    卡死:格断都是高电平,卡死是两个格断之间都是低电平
    解决:判断K2是否放开



扫描延时造成的读取错误
    解决1:在延时函数中加入K2的触发判断,触发就运行编码器的数值读取函数
    解决2:使用外部的中断向量控制器
           通过K2产生低电平触发,使得IO端口一个外部中断,在中断处理函数中进行编码器的数值读取

4.I2C总线(lm75a器件驱动)数码管显示温度值

对开发板上面的跳线进行设置

数码管:跳线处在短接状态

I2C总线:的两个跳线处在短接状态

l2C格式:器件地址+子地址+数据

温度传感器数码管显示程序 

 

 

 新建文件夹

Basic文件夹——>i2c文件夹——>i2c.c i2c.h

//器件的驱动:调用i2c.c的驱动程序,用来发送和接收器件所需要的数据,最终实现器件的通信(读取温度值)
Hardware文件夹——>LM75A文件夹——>lm75a.c lm75a.h



在Lib文件夹下面添加:stm32f10x_i2c.c

I2C总线的驱动程序,对固件库的组合调用

l2C_Configuration();    //l2C初始化

12C_SAND_BUFFER();      //I2C发送数据串

l2C_SAND_BYTE();        //I2C发送一个字节

12C_READ_BUFFER();      //I2C读取数据串

l2C_READ_BYTE();        //l2C读取一个字节

i2c.h

#ifndef __I2C_H
#define __I2C_H	 
#include "sys.h"

#define I2CPORT		GPIOB	//定义IO接口
#define I2C_SCL		GPIO_Pin_6	//定义IO接口
#define I2C_SDA		GPIO_Pin_7	//定义IO接口

#define HostAddress	0xc0	//总线主机的器件地址
#define BusSpeed	200000	//总线速度(不高于400000)


//I2C总线的驱动程序,对固件库的组合调用
void I2C_Configuration(void);
void I2C_SAND_BUFFER(u8 SlaveAddr, u8 WriteAddr, u8* pBuffer, u16 NumByteToWrite);
void I2C_SAND_BYTE(u8 SlaveAddr,u8 writeAddr,u8 pBuffer);
void I2C_READ_BUFFER(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead);
u8 I2C_READ_BYTE(u8 SlaveAddr,u8 readAddr);
		 				    
#endif

i2c.c

#include "i2c.h"


void I2C_GPIO_Init(void){ //I2C接口初始化    包含在void I2C_Configuration(void)
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);     //启动I2C功能 
    GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA;         //选择端口号                      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;          //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(I2CPORT, &GPIO_InitStructure);
}

void I2C_Configuration(void){ //I2C初始化
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_GPIO_Init(); //先设置GPIO接口的状态
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置为I2C模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_OwnAddress1 = HostAddress; //主机地址(从机不得用此地址)
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//允许应答
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址模式
	I2C_InitStructure.I2C_ClockSpeed = BusSpeed; //总线速度设置 	
	I2C_Init(I2C1,&I2C_InitStructure);
	I2C_Cmd(I2C1,ENABLE);//开启I2C					
}

void I2C_SAND_BUFFER(u8 SlaveAddr,u8 WriteAddr,u8* pBuffer,u16 NumByteToWrite){ //I2C发送数据串(器件地址,寄存器,内部地址,数量)
	I2C_GenerateSTART(I2C1,ENABLE);//产生起始位
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Transmitter);//发送器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除EV6
	I2C_SendData(I2C1,WriteAddr); //内部功能地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄存器非空,数据寄存器已空,产生EV8,发送数据到DR既清除该事件
	while(NumByteToWrite--){ //循环发送数据	
		I2C_SendData(I2C1,*pBuffer); //发送数据
		pBuffer++; //数据指针移位
		while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除EV8
	}
	I2C_GenerateSTOP(I2C1,ENABLE);//产生停止信号
}
void I2C_SAND_BYTE(u8 SlaveAddr,u8 writeAddr,u8 pBuffer){ //I2C发送一个字节(从地址,内部地址,内容)
	I2C_GenerateSTART(I2C1,ENABLE); //发送开始信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //等待完成	
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //发送从器件地址及状态(写入)
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待完成	
	I2C_SendData(I2C1,writeAddr); //发送从器件内部寄存器地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	
	I2C_SendData(I2C1,pBuffer); //发送要写入的内容
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	
	I2C_GenerateSTOP(I2C1,ENABLE); //发送结束信号
}
void I2C_READ_BUFFER(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead){ //I2C读取数据串(器件地址,寄存器,内部地址,数量)
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	I2C_GenerateSTART(I2C1,ENABLE);//开启信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));	//清除 EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //写入器件地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6
	I2C_Cmd(I2C1,ENABLE);
	I2C_SendData(I2C1,readAddr); //发送读的地址
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //清除 EV8
	I2C_GenerateSTART(I2C1,ENABLE); //开启信号
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5
	I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); //将器件地址传出,主机为读
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //清除EV6
	while(NumByteToRead){
		if(NumByteToRead == 1){ //只剩下最后一个数据时进入 if 语句
			I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
			I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位
		}
		if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)){ //读取数据
			*pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBuffer
			pBuffer++; //指针移位
			NumByteToRead--; //字节数减 1 
		}
	}
	I2C_AcknowledgeConfig(I2C1,ENABLE);
}
u8 I2C_READ_BYTE(u8 SlaveAddr,u8 readAddr){ //I2C读取一个字节
	u8 a;
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); 
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
	I2C_Cmd(I2C1,ENABLE);
	I2C_SendData(I2C1,readAddr);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	I2C_GenerateSTART(I2C1,ENABLE);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Receiver);
	while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
	I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位
	I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位
	a = I2C_ReceiveData(I2C1);
	return a;
}

读取温度值 

 

 

 

lm75a.h

#ifndef __LM75A_H
#define __LM75A_H	 
#include "sys.h"
#include "i2c.h"


#define LM75A_ADD	0x9E	//器件地址



void LM75A_GetTemp(u8 *Tempbuffer);//读温度
void LM75A_POWERDOWN(void); //掉电模式
		 				    
#endif

lm75a.c

#include "lm75a.h"



//读出LM75A的温度值(-55~125摄氏度)
//温度正负号(0正1负),温度整数,温度小数(点后2位)依次放入*Tempbuffer(十进制)
void LM75A_GetTemp(u8 *Tempbuffer){   
    u8 buf[2]; //温度值储存   
    u8 t=0,a=0;   
    I2C_READ_BUFFER(LM75A_ADD,0x00,buf,2); //读出温度值(器件地址,子地址,数据储存器,字节数)



	t = buf[0]; //处理温度整数部分,0~125度
	*Tempbuffer = 0; //温度值为正值
	if(t & 0x80){ //判断温度是否是负(MSB表示温度符号)
		*Tempbuffer = 1; //温度值为负值
		t = ~t; t++; //计算补码(原码取反后加1)
	}
	if(t & 0x01){ a=a+1; } //从高到低按位加入温度积加值(0~125)
	if(t & 0x02){ a=a+2; }
	if(t & 0x04){ a=a+4; }
	if(t & 0x08){ a=a+8; }
	if(t & 0x10){ a=a+16; }
	if(t & 0x20){ a=a+32; }
	if(t & 0x40){ a=a+64; }
	Tempbuffer++;
	*Tempbuffer = a;
	a = 0;



	t = buf[1]; //处理小数部分,取0.125精度的前2位(12、25、37、50、62、75、87)
	if(t & 0x20){ a=a+12; }
	if(t & 0x40){ a=a+25; }
	if(t & 0x80){ a=a+50; }
	Tempbuffer++;
	*Tempbuffer = a;   
}

//LM75进入掉电模式,再次调用LM75A_GetTemp();即可正常工作
//建议只在需要低功耗情况下使用
void LM75A_POWERDOWN(void){// 
    I2C_SAND_BYTE(LM75A_ADD,0x01,1); //设置寄存器写入开启掉电模式
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "TM1640.h"

#include "lm75a.h"

int main (void){//主程序
	u8 buffer[3];    //符号 + 整数值 + 小数值
	u8 c=0x01;
	RCC_Configuration(); //系统时钟初始化 

	I2C_Configuration();//I2C初始化

	TM1640_Init(); //TM1640初始化
	TM1640_display(0,20); //初始显示内容
	TM1640_display(1,20);
	TM1640_display(2,20);
	TM1640_display(3,20);
	TM1640_display(4,20);
	TM1640_display(5,20);
	TM1640_display(6,20);
	TM1640_display(7,20);

	while(1){
		LM75A_GetTemp(buffer); //读取LM75A的温度数据
		
            //第0位:存放正负号,这里没有使用
         //显示数值
            //第1位:整数值	十位 个位
		    TM1640_display(0,buffer[1]/10);
		    TM1640_display(1,buffer[1]%10+10);
            //第2位:小数值	十位 个位
		    TM1640_display(2,buffer[2]/10);
		    TM1640_display(3,buffer[2]%10);

		TM1640_led(c); //与TM1640连接的8个LED全亮
		c<<=1; //数据左移 流水灯
		if(c==0x00)c=0x01; //8个灯显示完后重新开始
		delay_ms(150); //延时
	}
}

三:OLED屏显示(采用L2C通信 体积小)

可以显示:图片、数字、汉字、字母..... 

1.温度值OLED屏汉字、数字、字母显示程序

 新建文件夹

Hardware文件夹——>OLED0561文件夹——>ASCII_8x16.h oled0561.h oled0561.c  


ASCII_8x16.h:字库文件   

 

oled0561.h

#ifndef __OLED_H
#define __OLED_H	 
#include "sys.h"
#include "i2c.h"

#define OLED0561_ADD	0x78  // OLED的I2C地址(禁止修改)    定义屏幕的器件地址
#define COM				0x00  // OLED 指令(禁止修改)
#define DAT 			0x40  // OLED 数据(禁止修改)


//屏幕的驱动程序
void OLED0561_Init(void);//初始化
void OLED_DISPLAY_ON (void);//OLED屏开显示
void OLED_DISPLAY_OFF (void);//OLED屏关显示
void OLED_DISPLAY_LIT (u8 x);//OLED屏亮度设置(0~255)
void OLED_DISPLAY_CLEAR(void);//清屏操作
void OLED_DISPLAY_8x16(u8 x,u8 y,u16 w);//显示8x16的单个字符 
void OLED_DISPLAY_8x16_BUFFER(u8 row,u8 *str);//显示8x16的字符串


		 				    
#endif

oled0561.c

#include "oled0561.h"
#include "ASCII_8x16.h" //引入字体 ASCII


void OLED0561_Init (void){//OLED屏开显示初始化
	OLED_DISPLAY_OFF(); //OLED关显示    避免出现乱码
	OLED_DISPLAY_CLEAR(); //清空屏幕内容
	OLED_DISPLAY_ON(); //OLED屏初始值设置并开显示

}
void OLED_DISPLAY_ON (void){//OLED屏初始值设置并开显示
	u8 buf[28]={
	0xae,//0xae:关显示,0xaf:开显示
    0x00,0x10,//开始地址(双字节)       
	0xd5,0x80,//显示时钟频率?
	0xa8,0x3f,//复用率?
	0xd3,0x00,//显示偏移?
	0XB0,//写入页位置(0xB0~7)
	0x40,//显示开始线
	0x8d,0x14,//VCC电源
	0xa1,//设置段重新映射?
	0xc8,//COM输出方式?
	0xda,0x12,//COM输出方式?
	0x81,0xff,//对比度,指令:0x81,数据:0~255(255最高)
	0xd9,0xf1,//充电周期?
	0xdb,0x30,//VCC电压输出
	0x20,0x00,//水平寻址设置
	0xa4,//0xa4:正常显示,0xa5:整体点亮
	0xa6,//0xa6:正常显示,0xa7:反色显示
	0xaf//0xae:关显示,0xaf:开显示
	}; //
	I2C_SAND_BUFFER(OLED0561_ADD,COM,buf,28);
}
void OLED_DISPLAY_OFF (void){//OLED屏关显示
	u8 buf[3]={
		0xae,//0xae:关显示,0xaf:开显示
		0x8d,0x10,//VCC电源
	}; //
	I2C_SAND_BUFFER(OLED0561_ADD,COM,buf,3);
}
void OLED_DISPLAY_LIT (u8 x){//OLED屏亮度设置(0~255)
	I2C_SAND_BYTE(OLED0561_ADD,COM,0x81);
	I2C_SAND_BYTE(OLED0561_ADD,COM,x);//亮度值
}
void OLED_DISPLAY_CLEAR(void){//清屏操作
	u8 j,t;
	for(t=0xB0;t<0xB8;t++){	//设置起始页地址为0xB0
		I2C_SAND_BYTE(OLED0561_ADD,COM,t); 	//页地址(从0xB0到0xB7)
		I2C_SAND_BYTE(OLED0561_ADD,COM,0x10); //起始列地址的高4位
		I2C_SAND_BYTE(OLED0561_ADD,COM,0x00);	//起始列地址的低4位
		for(j=0;j<132;j++){	//整页内容填充
 			I2C_SAND_BYTE(OLED0561_ADD,DAT,0x00);
 		}
	}
}

//显示英文与数字8*16的ASCII码
//取模大小为16*16,取模方式为“从左到右从上到下”“纵向8点下高位”
void OLED_DISPLAY_8x16(u8 x,     //显示英文的页坐标(从0到7)(此处不可修改)
					   u8 y,     //显示英文的列坐标(从0到63)
					   u16 w){   //要显示英文的编号
	u8 j,t,c=0;
	y=y+2; //因OLED屏的内置驱动芯片是从0x02列作为屏上最左一列,所以要加上偏移量
	for(t=0;t<2;t++){
		I2C_SAND_BYTE(OLED0561_ADD,COM,0xb0+x); //页地址(从0xB0到0xB7)
		I2C_SAND_BYTE(OLED0561_ADD,COM,y/16+0x10); //起始列地址的高4位
		I2C_SAND_BYTE(OLED0561_ADD,COM,y%16);	//起始列地址的低4位
		for(j=0;j<8;j++){ //整页内容填充
 			I2C_SAND_BYTE(OLED0561_ADD,DAT,ASCII_8x16[(w*16)+c-512]);//为了和ASII表对应要减512
			c++;}x++; //页地址加1
	}
}
//向LCM发送一个字符串,长度64字符之内。
//应用:OLED_DISPLAY_8_16_BUFFER(0," DoYoung Studio"); 
void OLED_DISPLAY_8x16_BUFFER(u8 row,u8 *str){
	u8 r=0;
	while(*str != '\0'){
		OLED_DISPLAY_8x16(row,r*8,*str++);
		r++;
    }	
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "lm75a.h"

#include "oled0561.h"

int main (void){//主程序
	u8 buffer[3];
	delay_ms(100); //上电时等待其他器件就绪    因为有启动时间,在启动时间内各个芯片是不能进行控制的
	RCC_Configuration(); //系统时钟初始化 

	I2C_Configuration();//I2C初始化
	LM75A_GetTemp(buffer); //读取LM75A的温度数据

	OLED0561_Init(); //OLED初始化

	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6,"  Temp:"); //显示字符串


    //实时刷新显示温度值
	while(1){
		LM75A_GetTemp(buffer); //读取LM75A的温度数据
		
        //读取第0个:正负号	          显示第几行 显示第几列  显示内容
		if(buffer[0])OLED_DISPLAY_8x16(6,7*8,'-'); //如果第1组为1即是负温度  是负温度加上“-”号
        //读取第1个:显示温度值    加上偏移量和ASCLL码的数字相对应
        //整数
		    OLED_DISPLAY_8x16(6,8*8,buffer[1]/10+0x30);//十位    单个字符
		    OLED_DISPLAY_8x16(6,9*8,buffer[1]%10+0x30);//个位

		OLED_DISPLAY_8x16(6,10*8,'.');

        //小数
		    OLED_DISPLAY_8x16(6,11*8,buffer[2]/10+0x30);//十位
		    OLED_DISPLAY_8x16(6,12*8,buffer[2]%10+0x30);//个位

		    OLED_DISPLAY_8x16(6,13*8,'C');//

		delay_ms(200); //延时
	}
}

ASCII_8x16.h        ASCII码


#ifndef __ASCII_8x16_H
#define __ASCII_8x16_H	 


// ------------------  ASCII字模的数据表 ------------------------ //
// 码表从0x20~0x7e                                                //
// 字库:  纵向取模下高位// (调用时要减512)
// -------------------------------------------------------------- //
const u8 ASCII_8x16[] =  {            // ASCII
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // - -
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x00,0x00,0x38,0xFC,0xFC,0x38,0x00,0x00,  // -!-
	0x00,0x00,0x00,0x0D,0x0D,0x00,0x00,0x00,

	0x00,0x0E,0x1E,0x00,0x00,0x1E,0x0E,0x00,  // -"-
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x20,0xF8,0xF8,0x20,0xF8,0xF8,0x20,0x00,  // -#-
	0x02,0x0F,0x0F,0x02,0x0F,0x0F,0x02,0x00,

	0x38,0x7C,0x44,0x47,0x47,0xCC,0x98,0x00,  // -$-
	0x03,0x06,0x04,0x1C,0x1C,0x07,0x03,0x00,

	0x30,0x30,0x00,0x80,0xC0,0x60,0x30,0x00,  // -%-
	0x0C,0x06,0x03,0x01,0x00,0x0C,0x0C,0x00,

	0x80,0xD8,0x7C,0xE4,0xBC,0xD8,0x40,0x00,  // -&-
	0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,

	0x00,0x10,0x1E,0x0E,0x00,0x00,0x00,0x00,  // -'-
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x00,0x00,0xF0,0xF8,0x0C,0x04,0x00,0x00,  // -(-
	0x00,0x00,0x03,0x07,0x0C,0x08,0x00,0x00,

	0x00,0x00,0x04,0x0C,0xF8,0xF0,0x00,0x00,  // -)-
	0x00,0x00,0x08,0x0C,0x07,0x03,0x00,0x00,

	0x80,0xA0,0xE0,0xC0,0xC0,0xE0,0xA0,0x80,  // -*-
	0x00,0x02,0x03,0x01,0x01,0x03,0x02,0x00,

	0x00,0x80,0x80,0xE0,0xE0,0x80,0x80,0x00,  // -+-
	0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,

	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // -,-
	0x00,0x00,0x10,0x1E,0x0E,0x00,0x00,0x00,

	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,  // ---
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // -.-
	0x00,0x00,0x00,0x0C,0x0C,0x00,0x00,0x00,

	0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x00,  // -/-
	0x0C,0x06,0x03,0x01,0x00,0x00,0x00,0x00,

	0xF8,0xFC,0x04,0xC4,0x24,0xFC,0xF8,0x00,  // -0-
	0x07,0x0F,0x09,0x08,0x08,0x0F,0x07,0x00,

	0x00,0x10,0x18,0xFC,0xFC,0x00,0x00,0x00,  // -1-
	0x00,0x08,0x08,0x0F,0x0F,0x08,0x08,0x00,

	0x08,0x0C,0x84,0xC4,0x64,0x3C,0x18,0x00,  // -2-
	0x0E,0x0F,0x09,0x08,0x08,0x0C,0x0C,0x00,

	0x08,0x0C,0x44,0x44,0x44,0xFC,0xB8,0x00,  // -3-
	0x04,0x0C,0x08,0x08,0x08,0x0F,0x07,0x00,

	0xC0,0xE0,0xB0,0x98,0xFC,0xFC,0x80,0x00,  // -4-
	0x00,0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,

	0x7C,0x7C,0x44,0x44,0xC4,0xC4,0x84,0x00,  // -5-
	0x04,0x0C,0x08,0x08,0x08,0x0F,0x07,0x00,

	0xF0,0xF8,0x4C,0x44,0x44,0xC0,0x80,0x00,  // -6-
	0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,

	0x0C,0x0C,0x04,0x84,0xC4,0x7C,0x3C,0x00,  // -7-
	0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,0x00,

	0xB8,0xFC,0x44,0x44,0x44,0xFC,0xB8,0x00,  // -8-
	0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,

	0x38,0x7C,0x44,0x44,0x44,0xFC,0xF8,0x00,  // -9-
	0x00,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,

	0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,  // -:-
	0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,

	0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,  // -;-
	0x00,0x00,0x08,0x0E,0x06,0x00,0x00,0x00,

	0x00,0x80,0xC0,0x60,0x30,0x18,0x08,0x00,  // -<-
	0x00,0x00,0x01,0x03,0x06,0x0C,0x08,0x00,

	0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,  // -=-
	0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,

	0x00,0x08,0x18,0x30,0x60,0xC0,0x80,0x00,  // ->-
	0x00,0x08,0x0C,0x06,0x03,0x01,0x00,0x00,

	0x18,0x1C,0x04,0xC4,0xE4,0x3C,0x18,0x00,  // -?-
	0x00,0x00,0x00,0x0D,0x0D,0x00,0x00,0x00,

	0xF0,0xF8,0x08,0xC8,0xC8,0xF8,0xF0,0x00,  // -@-
	0x07,0x0F,0x08,0x0B,0x0B,0x0B,0x01,0x00,

	0xE0,0xF0,0x98,0x8C,0x98,0xF0,0xE0,0x00,  // -A-
	0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,

	0x04,0xFC,0xFC,0x44,0x44,0xFC,0xB8,0x00,  // -B-
	0x08,0x0F,0x0F,0x08,0x08,0x0F,0x07,0x00,

	0xF0,0xF8,0x0C,0x04,0x04,0x0C,0x18,0x00,  // -C-
	0x03,0x07,0x0C,0x08,0x08,0x0C,0x06,0x00,

	0x04,0xFC,0xFC,0x04,0x0C,0xF8,0xF0,0x00,  // -D-
	0x08,0x0F,0x0F,0x08,0x0C,0x07,0x03,0x00,

	0x04,0xFC,0xFC,0x44,0xE4,0x0C,0x1C,0x00,  // -E-
	0x08,0x0F,0x0F,0x08,0x08,0x0C,0x0E,0x00,

	0x04,0xFC,0xFC,0x44,0xE4,0x0C,0x1C,0x00,  // -F-
	0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,

	0xF0,0xF8,0x0C,0x84,0x84,0x8C,0x98,0x00,  // -G-
	0x03,0x07,0x0C,0x08,0x08,0x07,0x0F,0x00,

	0xFC,0xFC,0x40,0x40,0x40,0xFC,0xFC,0x00,  // -H-
	0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,

	0x00,0x00,0x04,0xFC,0xFC,0x04,0x00,0x00,  // -I-
	0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,

	0x00,0x00,0x00,0x04,0xFC,0xFC,0x04,0x00,  // -J-
	0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,0x00,

	0x04,0xFC,0xFC,0xC0,0xF0,0x3C,0x0C,0x00,  // -K-
	0x08,0x0F,0x0F,0x00,0x01,0x0F,0x0E,0x00,

	0x04,0xFC,0xFC,0x04,0x00,0x00,0x00,0x00,  // -L-
	0x08,0x0F,0x0F,0x08,0x08,0x0C,0x0E,0x00,

	0xFC,0xFC,0x38,0x70,0x38,0xFC,0xFC,0x00,  // -M-
	0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,

	0xFC,0xFC,0x38,0x70,0xE0,0xFC,0xFC,0x00,  // -N-
	0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,

	0xF0,0xF8,0x0C,0x04,0x0C,0xF8,0xF0,0x00,  // -O-
	0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,

	0x04,0xFC,0xFC,0x44,0x44,0x7C,0x38,0x00,  // -P-
	0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,

	0xF8,0xFC,0x04,0x04,0x04,0xFC,0xF8,0x00,  // -Q-
	0x07,0x0F,0x08,0x0E,0x3C,0x3F,0x27,0x00,

	0x04,0xFC,0xFC,0x44,0xC4,0xFC,0x38,0x00,  // -R-
	0x08,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,

	0x18,0x3C,0x64,0x44,0xC4,0x9C,0x18,0x00,  // -S-
	0x06,0x0E,0x08,0x08,0x08,0x0F,0x07,0x00,

	0x00,0x1C,0x0C,0xFC,0xFC,0x0C,0x1C,0x00,  // -T-
	0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,

	0xFC,0xFC,0x00,0x00,0x00,0xFC,0xFC,0x00,  // -U-
	0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,

	0xFC,0xFC,0x00,0x00,0x00,0xFC,0xFC,0x00,  // -V-
	0x01,0x03,0x06,0x0C,0x06,0x03,0x01,0x00,

	0xFC,0xFC,0x00,0x80,0x00,0xFC,0xFC,0x00,  // -W-
	0x03,0x0F,0x0E,0x03,0x0E,0x0F,0x03,0x00,

	0x0C,0x3C,0xF0,0xC0,0xF0,0x3C,0x0C,0x00,  // -X-
	0x0C,0x0F,0x03,0x00,0x03,0x0F,0x0C,0x00,

	0x00,0x3C,0x7C,0xC0,0xC0,0x7C,0x3C,0x00,  // -Y-
	0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,

	0x1C,0x0C,0x84,0xC4,0x64,0x3C,0x1C,0x00,  // -Z-
	0x0E,0x0F,0x09,0x08,0x08,0x0C,0x0E,0x00,

	0x00,0x00,0xFC,0xFC,0x04,0x04,0x00,0x00,  // -[-
	0x00,0x00,0x0F,0x0F,0x08,0x08,0x00,0x00,

	0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,  // -\-
	0x00,0x00,0x00,0x01,0x03,0x07,0x0E,0x00,

	0x00,0x00,0x04,0x04,0xFC,0xFC,0x00,0x00,  // -]-
	0x00,0x00,0x08,0x08,0x0F,0x0F,0x00,0x00,

	0x08,0x0C,0x06,0x03,0x06,0x0C,0x08,0x00,  // -^-
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  // -_-
	0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,

	0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00,  // -`-
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x00,0xA0,0xA0,0xA0,0xE0,0xC0,0x00,0x00,  // -a-
	0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,

	0x04,0xFC,0xFC,0x20,0x60,0xC0,0x80,0x00,  // -b-
	0x08,0x0F,0x07,0x08,0x08,0x0F,0x07,0x00,

	0xC0,0xE0,0x20,0x20,0x20,0x60,0x40,0x00,  // -c-
	0x07,0x0F,0x08,0x08,0x08,0x0C,0x04,0x00,

	0x80,0xC0,0x60,0x24,0xFC,0xFC,0x00,0x00,  // -d-
	0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,

	0xC0,0xE0,0xA0,0xA0,0xA0,0xE0,0xC0,0x00,  // -e-
	0x07,0x0F,0x08,0x08,0x08,0x0C,0x04,0x00,

	0x40,0xF8,0xFC,0x44,0x0C,0x18,0x00,0x00,  // -f-
	0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,

	0xC0,0xE0,0x20,0x20,0xC0,0xE0,0x20,0x00,  // -g-
	0x27,0x6F,0x48,0x48,0x7F,0x3F,0x00,0x00,

	0x04,0xFC,0xFC,0x40,0x20,0xE0,0xC0,0x00,  // -h-
	0x08,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,

	0x00,0x00,0x20,0xEC,0xEC,0x00,0x00,0x00,  // -i-
	0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,

	0x00,0x00,0x00,0x00,0x20,0xEC,0xEC,0x00,  // -j-
	0x00,0x30,0x70,0x40,0x40,0x7F,0x3F,0x00,

	0x04,0xFC,0xFC,0x80,0xC0,0x60,0x20,0x00,  // -k-
	0x08,0x0F,0x0F,0x01,0x03,0x0E,0x0C,0x00,

	0x00,0x00,0x04,0xFC,0xFC,0x00,0x00,0x00,  // -l-
	0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,

	0xE0,0xE0,0x60,0xC0,0x60,0xE0,0xC0,0x00,  // -m-
	0x0F,0x0F,0x00,0x0F,0x00,0x0F,0x0F,0x00,

	0x20,0xE0,0xC0,0x20,0x20,0xE0,0xC0,0x00,  // -n-
	0x00,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,

	0xC0,0xE0,0x20,0x20,0x20,0xE0,0xC0,0x00,  // -o-
	0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,

	0x20,0xE0,0xC0,0x20,0x20,0xE0,0xC0,0x00,  // -p-
	0x40,0x7F,0x7F,0x48,0x08,0x0F,0x07,0x00,

	0xC0,0xE0,0x20,0x20,0xC0,0xE0,0x20,0x00,  // -q-
	0x07,0x0F,0x08,0x48,0x7F,0x7F,0x40,0x00,

	0x20,0xE0,0xC0,0x60,0x20,0x60,0xC0,0x00,  // -r-
	0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,

	0x40,0xE0,0xA0,0x20,0x20,0x60,0x40,0x00,  // -s-
	0x04,0x0C,0x09,0x09,0x0B,0x0E,0x04,0x00,

	0x20,0x20,0xF8,0xFC,0x20,0x20,0x00,0x00,  // -t-
	0x00,0x00,0x07,0x0F,0x08,0x0C,0x04,0x00,

	0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00,0x00,  // -u-
	0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,

	0x00,0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00,  // -v-
	0x00,0x03,0x07,0x0C,0x0C,0x07,0x03,0x00,

	0xE0,0xE0,0x00,0x00,0x00,0xE0,0xE0,0x00,  // -w-
	0x07,0x0F,0x0C,0x07,0x0C,0x0F,0x07,0x00,

	0x20,0x60,0xC0,0x80,0xC0,0x60,0x20,0x00,  // -x-
	0x08,0x0C,0x07,0x03,0x07,0x0C,0x08,0x00,

	0xE0,0xE0,0x00,0x00,0x00,0xE0,0xE0,0x00,  // -y-
	0x47,0x4F,0x48,0x48,0x68,0x3F,0x1F,0x00,

	0x60,0x60,0x20,0xA0,0xE0,0x60,0x20,0x00,  // -z-
	0x0C,0x0E,0x0B,0x09,0x08,0x0C,0x0C,0x00,

	0x00,0x40,0x40,0xF8,0xBC,0x04,0x04,0x00,  // -{-
	0x00,0x00,0x00,0x07,0x0F,0x08,0x08,0x00,

	0x00,0x00,0x00,0xBC,0xBC,0x00,0x00,0x00,  // -|-
	0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,

	0x00,0x04,0x04,0xBC,0xF8,0x40,0x40,0x00,  // -}-
	0x00,0x08,0x08,0x0F,0x07,0x00,0x00,0x00,

	0x08,0x0C,0x04,0x0C,0x08,0x0C,0x04,0x00,  // -~-
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	0x80,0xC0,0x60,0x30,0x60,0xC0,0x80,0x00,  // --
	0x07,0x07,0x04,0x04,0x04,0x07,0x07,0x00,
};

#endif

2.OLED屏幕汉字、图片显示程序

 新建文件夹

Hardware文件夹——>OLED0561文件夹——>CHS_16x16.h PIC1.h 


CHS_16x16.h:存放汉字数据库的   
PIC1.h:存放图片数据库的 

在oled561.c中加入了汉字和图片相关的驱动函数

LCD汉字字母数字图片取模板(LcmZimo)

可以生成:中文、数字、字母、图片的相关数据


也可以通过按钮或可视化点击操作:进行设置显示类型类容形状

-----------------------------------------------------------------
ASCLL数据表生成    设置参数:
    1.输出格式——>结构型格式:从左到右从上到下
    2.取模方式——>纵向8点下高位
    3.ASC/汉字选择——>ASCLL
    4.字库选择——>Asc8X16E
    5.点击魔法按钮:参数确认——>点击ASC按钮



汉字数据表生成    设置参数:
    1.输出格式——>结构型格式:从左到右从上到下
    2.取模方式——>纵向8点下高位
    3.ASC/汉字选择——>汉字
    4.字库选择——>宋体16点阵
    5.点击魔法按钮:参数确认——>点击ASC按钮
    6.在下方输入我们想要的汉字——>点击A输入字串



图片数据表生成    设置参数:
    1.输出格式——>结构型格式:从左到右从上到下
    2.取模方式——>纵向8点下高位
    3.输出大小——>X128 Y64
    4.点击魔法按钮:参数确认
    5.点击载入图片——>从电脑本地导入图片打开(只能.bmp格式  无灰度单色位图  尺寸128*644)
    6.点击数据保存——>然后取一个名字 点击保存

oled0561.h

#ifndef __OLED_H
#define __OLED_H	 
#include "sys.h"
#include "i2c.h"

#define OLED0561_ADD	0x78  // OLED的I2C地址(禁止修改)
#define COM				0x00  // OLED 指令(禁止修改)
#define DAT 			0x40  // OLED 数据(禁止修改)

void OLED0561_Init(void);//初始化
void OLED_DISPLAY_ON (void);//OLED屏开显示
void OLED_DISPLAY_OFF (void);//OLED屏关显示
void OLED_DISPLAY_LIT (u8 x);//OLED屏亮度设置(0~255)
void OLED_DISPLAY_CLEAR(void);//清屏操作
void OLED_DISPLAY_8x16(u8 x,u8 y,u16 w);//显示8x16的单个字符 
void OLED_DISPLAY_8x16_BUFFER(u8 row,u8 *str);//显示8x16的字符串

void OLED_DISPLAY_16x16(u8 x,u8 y,u16 w); //汉字显示
void OLED_DISPLAY_PIC1(void);//图片显示
		 				    
#endif

oled0561.c

#include "oled0561.h"
#include "ASCII_8x16.h" //引入字体 ASCII

#include "CHS_16x16.h" //引入汉字字体 
#include "PIC1.h" //引入图片


void OLED0561_Init (void){//OLED屏开显示初始化
	OLED_DISPLAY_OFF(); //OLED关显示
	OLED_DISPLAY_CLEAR(); //清空屏幕内容
	OLED_DISPLAY_ON(); //OLED屏初始值设置并开显示
}
void OLED_DISPLAY_ON (void){//OLED屏初始值设置并开显示
	u8 buf[28]={
	0xae,//0xae:关显示,0xaf:开显示
    0x00,0x10,//开始地址(双字节)       
	0xd5,0x80,//显示时钟频率?
	0xa8,0x3f,//复用率?
	0xd3,0x00,//显示偏移?
	0XB0,//写入页位置(0xB0~7)
	0x40,//显示开始线
	0x8d,0x14,//VCC电源
	0xa1,//设置段重新映射?
	0xc8,//COM输出方式?
	0xda,0x12,//COM输出方式?
	0x81,0xff,//对比度,指令:0x81,数据:0~255(255最高)
	0xd9,0xf1,//充电周期?
	0xdb,0x30,//VCC电压输出
	0x20,0x00,//水平寻址设置
	0xa4,//0xa4:正常显示,0xa5:整体点亮
	0xa6,//0xa6:正常显示,0xa7:反色显示
	0xaf//0xae:关显示,0xaf:开显示
	}; //
	I2C_SAND_BUFFER(OLED0561_ADD,COM,buf,28);
}
void OLED_DISPLAY_OFF (void){//OLED屏关显示
	u8 buf[3]={
		0xae,//0xae:关显示,0xaf:开显示
		0x8d,0x10,//VCC电源
	}; //
	I2C_SAND_BUFFER(OLED0561_ADD,COM,buf,3);
}
void OLED_DISPLAY_LIT (u8 x){//OLED屏亮度设置(0~255)
	I2C_SAND_BYTE(OLED0561_ADD,COM,0x81);
	I2C_SAND_BYTE(OLED0561_ADD,COM,x);//亮度值
}
void OLED_DISPLAY_CLEAR(void){//清屏操作
	u8 j,t;
	for(t=0xB0;t<0xB8;t++){	//设置起始页地址为0xB0
		I2C_SAND_BYTE(OLED0561_ADD,COM,t); 	//页地址(从0xB0到0xB7)
		I2C_SAND_BYTE(OLED0561_ADD,COM,0x10); //起始列地址的高4位
		I2C_SAND_BYTE(OLED0561_ADD,COM,0x00);	//起始列地址的低4位
		for(j=0;j<132;j++){	//整页内容填充
 			I2C_SAND_BYTE(OLED0561_ADD,DAT,0x00);
 		}
	}
}

//显示英文与数字8*16的ASCII码
//取模大小为8*16,取模方式为“从左到右从上到下”“纵向8点下高位”
void OLED_DISPLAY_8x16(u8 x, //显示汉字的页坐标(从0到7)(此处不可修改)
						u8 y, //显示汉字的列坐标(从0到128)
						u16 w){ //要显示汉字的编号
	u8 j,t,c=0;
	y=y+2; //因OLED屏的内置驱动芯片是从0x02列作为屏上最左一列,所以要加上偏移量
	for(t=0;t<2;t++){
		I2C_SAND_BYTE(OLED0561_ADD,COM,0xb0+x); //页地址(从0xB0到0xB7)
		I2C_SAND_BYTE(OLED0561_ADD,COM,y/16+0x10); //起始列地址的高4位
		I2C_SAND_BYTE(OLED0561_ADD,COM,y%16);	//起始列地址的低4位
		for(j=0;j<8;j++){ //整页内容填充
 			I2C_SAND_BYTE(OLED0561_ADD,DAT,ASCII_8x16[(w*16)+c-512]);//为了和ASII表对应要减512
			c++;}x++; //页地址加1
	}
}
//向LCM发送一个字符串,长度64字符之内。
//应用:OLED_DISPLAY_8_16_BUFFER(0," DoYoung Studio"); 
void OLED_DISPLAY_8x16_BUFFER(u8 row,u8 *str){
	u8 r=0;
	while(*str != '\0'){
		OLED_DISPLAY_8x16(row,r*8,*str++);
		r++;
    }	
}

//----- 用于汉字显示的程序 ------//

//显示汉字16*16
//取模大小为16*16,取模方式为“从左到右从上到下”“纵向8点下高位”
void OLED_DISPLAY_16x16(u8 x, //显示汉字的页坐标(从0xB0到0xB7)
			u8 y, //显示汉字的列坐标(从0到63)
			u16 w){ //要显示汉字的编号
	u8 j,t,c=0;
	for(t=0;t<2;t++){
		I2C_SAND_BYTE(OLED0561_ADD,COM,0xb0+x); //页地址(从0xB0到0xB7)
		I2C_SAND_BYTE(OLED0561_ADD,COM,y/16+0x10); //起始列地址的高4位
		I2C_SAND_BYTE(OLED0561_ADD,COM,y%16);	//起始列地址的低4位
		for(j=0;j<16;j++){ //整页内容填充
 			I2C_SAND_BYTE(OLED0561_ADD,DAT,GB_16[(w*32)+c]);
			c++;}x++; //页地址加1
	}
	I2C_SAND_BYTE(OLED0561_ADD,COM,0xAF); //开显示 
}


//----- 用于图片显示的程序 ------//
void OLED_DISPLAY_PIC1(void){ //显示全屏图片
	u8 m,i;
	for(m=0;m<8;m++){//
		I2C_SAND_BYTE(OLED0561_ADD,COM,0xb0+m);
		I2C_SAND_BYTE(OLED0561_ADD,COM,0x10); //起始列地址的高4位
		I2C_SAND_BYTE(OLED0561_ADD,COM,0x02);	//起始列地址的低4位
		for(i=0;i<128;i++){//送入128次图片显示内容
			I2C_SAND_BYTE(OLED0561_ADD,DAT,PIC1[i+m*128]);}
	}
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "lm75a.h"

#include "oled0561.h"

int main (void){//主程序
	u8 buffer[3];
	delay_ms(100); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 

	I2C_Configuration();//I2C初始化
	LM75A_GetTemp(buffer); //读取LM75A的温度数据

	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_LIT(100);//亮度设置

	OLED_DISPLAY_PIC1();//显示全屏图片
	delay_ms(1000); //延时
	OLED_DISPLAY_CLEAR();
	OLED_DISPLAY_8x16_BUFFER(0,"   liuxinleiup "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6,"  Temp:"); //显示字符串

	OLED_DISPLAY_16x16(2,2*16,0);//汉字显示
	OLED_DISPLAY_16x16(2,3*16,1);
	OLED_DISPLAY_16x16(2,4*16,2);
	OLED_DISPLAY_16x16(2,5*16,3);

	while(1){
		LM75A_GetTemp(buffer); //读取LM75A的温度数据
			
		if(buffer[0])OLED_DISPLAY_8x16(6,7*8,'-'); //如果第1组为1即是负温度
		OLED_DISPLAY_8x16(6,8*8,buffer[1]/10+0x30);//显示温度值
		OLED_DISPLAY_8x16(6,9*8,buffer[1]%10+0x30);//
		OLED_DISPLAY_8x16(6,10*8,'.');//
		OLED_DISPLAY_8x16(6,11*8,buffer[2]/10+0x30);//
		OLED_DISPLAY_8x16(6,12*8,buffer[2]%10+0x30);//
		OLED_DISPLAY_8x16(6,13*8,'C');//

		delay_ms(200); //延时
	}
}

CHS_16x16.h        汉字


#ifndef __CHS_16x16_H
#define __CHS_16x16_H	 

uc8 GB_16[] = {         // 数据表
"好", 0x10,0x10,0xF0,0x1F,0x10,0xF0,0x80,0x82,
      0x82,0x82,0xE2,0x92,0x8A,0xC6,0x80,0x00,
      0x40,0x22,0x15,0x08,0x14,0x63,0x00,0x00,
      0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,

"学", 0x40,0x30,0x11,0x96,0x90,0x90,0x91,0x96,
      0x90,0x90,0x98,0x14,0x13,0x50,0x30,0x00,
      0x04,0x04,0x04,0x04,0x04,0x44,0x84,0x7E,
      0x06,0x05,0x04,0x04,0x04,0x06,0x04,0x00,

"习", 0x04,0x04,0x04,0x04,0x14,0x24,0xC4,0x04,
      0x04,0x04,0x84,0x04,0x04,0xFE,0x04,0x00,
      0x00,0x00,0x08,0x18,0x04,0x04,0x02,0x02,
      0x01,0x21,0x40,0x80,0x40,0x3F,0x00,0x00,

"你", 0x40,0x20,0xF8,0x07,0x40,0x20,0x18,0x0F,
      0x08,0xC8,0x08,0x08,0x28,0x18,0x00,0x00,
      0x00,0x00,0xFF,0x00,0x00,0x08,0x04,0x43,
      0x80,0x7F,0x00,0x01,0x06,0x0C,0x00,0x00,

"在", 0x08,0x08,0x08,0x08,0xC8,0x38,0x0F,0x08,
      0x08,0xE8,0x08,0x88,0x08,0x0C,0x08,0x00,
      0x08,0x04,0x02,0xFF,0x00,0x40,0x41,0x41,
      0x41,0x7F,0x41,0x41,0x41,0x60,0x40,0x00,

"瞅", 0x00,0xFC,0x24,0x24,0xFE,0x24,0xA4,0xFE,
      0xA3,0x12,0x20,0xFE,0x20,0x10,0x18,0x00,
      0x00,0x7F,0x22,0x22,0x7F,0x02,0x01,0xFF,
      0x40,0x21,0x1C,0x03,0x1C,0x60,0x20,0x00,

"啥", 0x00,0xFC,0x04,0x04,0xFE,0xA4,0x90,0x98,
      0x94,0xF3,0x94,0x98,0x90,0xB0,0x10,0x00,
      0x00,0x1F,0x08,0x08,0x1F,0x00,0xFC,0x44,
      0x44,0x47,0x44,0x44,0xFE,0x04,0x00,0x00
};

// 汉字表:
// 好学习你在瞅啥

#endif

PIC1.h        图片

// 图片尺寸: 128 * 64                                                  //
uc8 PIC1[] =                  // 数据表
{
      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,
      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,
      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,0xF8,0xFC,0x1C,0x38,0x30,0x38,0x1C,
      0xFC,0xF8,0x00,0xF8,0xF8,0x00,0xF8,0xF8,
      0x00,0x00,0x00,0xF8,0xFC,0x8C,0x8C,0x8C,
      0x00,0x80,0xD0,0x50,0xF0,0xE0,0x00,0xF0,
      0xF0,0x20,0x30,0x00,0x00,0x80,0x80,0x00,
      0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x0C,
      0x12,0x24,0x12,0x0C,0x00,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x18,0x24,0x44,0x88,
      0x44,0x24,0x18,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,0x03,0x83,0x80,0x00,0x80,0x80,0x00,
      0x03,0x03,0x00,0x08,0x0B,0x0B,0x0F,0x07,
      0x80,0x40,0x80,0x40,0x83,0x03,0x03,0x03,
      0x00,0x00,0x03,0x03,0x03,0x03,0x00,0x03,
      0x83,0x80,0x80,0x80,0x80,0xC3,0xC3,0xC0,
      0xC3,0xC3,0xC0,0xC3,0xC3,0xC0,0xC0,0xC0,
      0xC0,0xC0,0x80,0x80,0x80,0xC0,0xC0,0x40,
      0x20,0x18,0x04,0x03,0x00,0x00,0x00,0x00,
      0x00,0x20,0x50,0xA0,0x50,0x20,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,0x03,0x04,0x08,0x11,0x08,0x04,0x03,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
      0x01,0x02,0x84,0xC2,0xE1,0x60,0x30,0x10,
      0x78,0xEC,0xFC,0xF6,0x3A,0x1B,0x05,0x85,
      0x97,0x13,0x12,0x13,0x13,0x11,0x11,0x11,
      0x11,0x11,0x11,0x01,0x81,0xFF,0xF9,0xF9,
      0xF2,0xC2,0x82,0x22,0x25,0x05,0x8D,0x8B,
      0x93,0xB3,0xE6,0xC6,0x8C,0x1C,0x18,0x38,
      0x70,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,
      0x00,0x00,0x00,0x18,0x24,0x48,0x24,0x18,
      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,0x80,0xE0,0xE0,0x70,0x30,0x18,
      0x18,0x1C,0x1C,0x1C,0x0C,0x1C,0x16,0x16,
      0x16,0x06,0x27,0x63,0x6D,0xAE,0xCE,0x0E,
      0x0E,0x0F,0x0F,0x0F,0x0E,0x06,0x06,0x07,
      0x07,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
      0x06,0x06,0x06,0x66,0x77,0x67,0x67,0xE7,
      0x07,0x07,0x07,0x06,0x06,0x06,0x07,0x07,
      0x87,0xC7,0x67,0x23,0x23,0x12,0x10,0x00,
      0x08,0x08,0x09,0x0B,0x0F,0x0E,0x0E,0x0C,
      0x3C,0x70,0xF0,0xC0,0x80,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,0xF0,0xF8,
      0xDE,0xFF,0xF6,0x36,0x11,0xE0,0xF0,0xF8,
      0xFC,0xFE,0xDE,0x1E,0x3F,0x3F,0x1E,0xFE,
      0xFE,0xFC,0xF8,0xF2,0xE4,0x08,0x01,0x07,
      0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
      0x00,0x00,0x00,0x00,0x00,0x3C,0xFF,0xE7,
      0x31,0x1C,0xE6,0xFF,0xFF,0xFF,0xFE,0xFE,
      0x1F,0x3F,0x3F,0x1F,0xFF,0xFE,0xFE,0xFC,
      0xF8,0xF8,0xF0,0x31,0xFB,0xFF,0xFE,0xFC,
      0x38,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,0x03,
      0x07,0x4F,0x4C,0x60,0x60,0x7F,0x7F,0x7F,
      0x7F,0x7D,0x4D,0x44,0x72,0x72,0x40,0x79,
      0x7D,0x7F,0x7F,0x7F,0x7F,0x7C,0x70,0x70,
      0x73,0x73,0x70,0x70,0x74,0x74,0x74,0x74,
      0x74,0x74,0x74,0x74,0x74,0x74,0x74,0x74,
      0x76,0x76,0x76,0x76,0x76,0x76,0x76,0x73,
      0x70,0x70,0x72,0x72,0x72,0x73,0x70,0x70,
      0x70,0x7C,0x7F,0x7F,0x7F,0x7F,0x7D,0x7D,
      0x62,0x72,0x72,0x60,0x68,0x7D,0x7F,0x7F,
      0x7F,0x7F,0x77,0x74,0x67,0x67,0x63,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,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,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
};

四:继电器控制(按键控制继电器程序

对开发板跳线进行设置

第一步:继电器的两个跳线处在短接状态

第二步:触摸按键使四个跳线处在短接状态

按A键继电器1打开;按B键继电器1关闭;按C键继电器2打开;按D键继电器2关闭;

 

端口输出高电平继电器吸合,端口输出低电平继电器断开 

 

 新建文件夹

Hardware文件夹——>RELAY文件夹——>relay.c relay.h

relay.h

#ifndef __RELAY_H
#define __RELAY_H	 
#include "sys.h"


#define RELAYPORT	GPIOA	//定义IO接口
#define RELAY1	GPIO_Pin_14	//定义IO接口
#define RELAY2	GPIO_Pin_13	//定义IO接口



void RELAY_Init(void);//继电器初始化
void RELAY_1(u8 c);//继电器控制1
void RELAY_2(u8 c);//继电器控制2
		 				    
#endif

relay.c

/*注意:
    本程序所占用的GPIO接口PA13、PA14上电后为JTAG功能,
    需要在RCC程序里启动AFIO时钟,再在RELAY_Init函数里加入:
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
    // 改变指定管脚的映射,完全禁用JTAG+SW-DP才能将JATG接口重定义为GPIO
*/




#include "relay.h"

void RELAY_Init(void){ //继电器的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | 
                           RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设时钟使能      
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//启动AFIO重映射功能时钟    
    GPIO_InitStructure.GPIO_Pin = RELAY1 | RELAY2; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(RELAYPORT, &GPIO_InitStructure);
	//必须将禁用JTAG功能才能做GPIO使用
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);// 改变指定管脚的映射,完全禁用JTAG+SW-DP
	GPIO_ResetBits(RELAYPORT,RELAY1 | RELAY2); //都为低电平(0) 初始为关继电器							
}

void RELAY_1(u8 c){ //继电器的控制程序(c=0继电器放开,c=1继电器吸合)
	GPIO_WriteBit(RELAYPORT,RELAY1,(BitAction)(c));//通过参数值写入接口
}
void RELAY_2(u8 c){ //继电器的控制程序(c=0继电器放开,c=1继电器吸合)
	GPIO_WriteBit(RELAYPORT,RELAY2,(BitAction)(c));//通过参数值写入接口
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"

#include "relay.h"

int main (void){//主程序
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化

	RELAY_Init();//继电器初始化

	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))RELAY_1(1); //当按键A按下时继电器1标志置位		
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B))RELAY_1(0); //当按键B按下时继电器1标志置位		
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C))RELAY_2(1); //当按键C按下时继电器2标志置位
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D))RELAY_2(0); //当按键D按下时继电器2标志置位
	}
}

五:步进电机控制

对开发板跳线进行设置

第一步:将步进电机的跳线帽全部短接(步进电机驱动电路和单片机IO端口进行连接)


第二步:触摸按键使四个跳线处在短接状态


第三步:找到步进电机,将步进电机的接口 插入到开发板中的步进电机接口

1.按键控制步进电机程序 

按下A键电机以很快的速度顺时针转动  松开停止转动;按下B键电机以很快的速度逆时针转动  松开停止转动;

按下C键电机以很慢的速度顺时针转动  松开停止转动;按下D键电机以很慢的速度逆时针转动  松开停止转动;

 

 

几线:几个头;几相:几条线

 

 

 

新建文件夹

Hardware文件夹——>STEP_MOTOR文件夹——>step_motor.c step_motor.h

step_motor.h

#ifndef __STEP_MOTOR_H
#define __STEP_MOTOR_H	 
#include "sys.h"
#include "delay.h"


#define STEP_MOTOR_PORT	GPIOB	//定义IO接口所在组
#define STEP_MOTOR_A	GPIO_Pin_3	//定义IO接口
#define STEP_MOTOR_B	GPIO_Pin_4	//定义IO接口
#define STEP_MOTOR_C	GPIO_Pin_8	//定义IO接口
#define STEP_MOTOR_D	GPIO_Pin_9	//定义IO接口



void STEP_MOTOR_Init(void);//初始化
void STEP_MOTOR_OFF (void);//断电状态
void STEP_MOTOR_4S (u8 speed);//固定位置(制动)
void STEP_MOTOR_4R (u8 speed);//
void STEP_MOTOR_4L (u8 speed);
void STEP_MOTOR_8R (u8 speed);
void STEP_MOTOR_8L (u8 speed);


		 				    
#endif

step_motor.c

#include "step_motor.h"

 


void STEP_MOTOR_Init(void){ //LED灯的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | 
                           RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设GPIO时钟使能      
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//启动AFIO重映射功能时钟    
    GPIO_InitStructure.GPIO_Pin = STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D; //选择端口                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(STEP_MOTOR_PORT, &GPIO_InitStructure);
	//必须将禁用JTAG功能才能做GPIO使用
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);// 改变指定管脚的映射,完全禁用JTAG+SW-DP	
	STEP_MOTOR_OFF(); //初始状态是断电状态 			
}

void STEP_MOTOR_OFF (void){//电机断电
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//各接口置0
}

//使电机转轴来回跳转
void STEP_MOTOR_4S (u8 speed){//电机固定位置
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A| STEP_MOTOR_C);	//各接口置0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_D); //各接口置1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C); //1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}

void STEP_MOTOR_4R (u8 speed){//电机顺时针,4拍,速度快,力小    A B C D
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A| STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}

void STEP_MOTOR_4L (u8 speed){//电机逆时针,4拍,速度快,力小     D C B A
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}


void STEP_MOTOR_8R (u8 speed){//电机顺时针,8拍,角度小,速度慢,力大
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}

void STEP_MOTOR_8L (u8 speed){//电机逆时针,8拍,角度小,速度慢,力大
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1
	delay_ms(speed); //延时
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0
	GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"

#include "step_motor.h"

int main (void){//主程序
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	STEP_MOTOR_Init();//步进电机初始化

	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))STEP_MOTOR_4R(3); //当按键A按下时步进电机4步右转		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B))STEP_MOTOR_4L(3); //当按键B按下时步进电机4步左转		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C))STEP_MOTOR_8R(3); //当按键C按下时步进电机8步右转
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D))STEP_MOTOR_8L(3); //当按键D按下时步进电机8步左转
		else STEP_MOTOR_OFF();//当没有按键时步进电机断电
	}
}

2.步进电机步数控制程序(精密控制)

按下A键电机逆时针精密的旋转一周360度并回到启点;按下B键电机顺时针精密的旋转一周360度并回到启点

按下C键逆时针微调角度、D键顺时针微调角度

step_motor.h

#ifndef __STEP_MOTOR_H
#define __STEP_MOTOR_H	 
#include "sys.h"
#include "delay.h"

extern u8 STEP; //定义单步计数 全局变量

#define STEP_MOTOR_PORT	GPIOB	//定义IO接口所在组
#define STEP_MOTOR_A	GPIO_Pin_3	//定义IO接口
#define STEP_MOTOR_B	GPIO_Pin_4	//定义IO接口
#define STEP_MOTOR_C	GPIO_Pin_8	//定义IO接口
#define STEP_MOTOR_D	GPIO_Pin_9	//定义IO接口



void STEP_MOTOR_Init(void);//初始化
void STEP_MOTOR_OFF (void);//断电状态
void STEP_MOTOR_8A (u8 a,u16 speed);    //单步运行程序
void STEP_MOTOR_NUM (u8 RL,u16 num,u8 speed);//电机按步数运行
void STEP_MOTOR_LOOP (u8 RL,u8 LOOP,u8 speed);//电机按圈数运行


		 				    
#endif

step_motor.c

#include "step_motor.h"

u8 STEP; 


void STEP_MOTOR_Init(void){ //接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | 
                           RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设GPIO时钟使能      
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//启动AFIO重映射功能时钟    
    GPIO_InitStructure.GPIO_Pin = STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D; //选择端口                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(STEP_MOTOR_PORT, &GPIO_InitStructure);
	//必须将禁用JTAG功能才能做GPIO使用
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);// 改变指定管脚的映射,完全禁用JTAG+SW-DP	
	STEP_MOTOR_OFF(); //初始状态是断电状态 			
}

void STEP_MOTOR_OFF (void){//电机断电
	GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//各接口置0
}

void STEP_MOTOR_8A (u8 a,u16 speed){//电机单步8拍
	switch (a){
		case 0:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1
			break;
		case 1:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1
			break;
		case 2:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1
			break;
		case 3:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1
			break;
		case 4:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1
			break;
		case 5:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1
			break;
		case 6:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1
			break;
		case 7:
		GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0
		GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1
			break;
		default:
			break;
	}
	delay_ms(speed); //延时
	STEP_MOTOR_OFF(); //进入断电状态,防电机过热
}

void STEP_MOTOR_NUM (u8 RL,u16 num,u8 speed){//电机按步数运行
	u16 i;
	for(i=0;i<num;i++){	
		if(RL==1){ //当RL=1右转,RL=0左转
			STEP++;
			if(STEP>7)STEP=0;
		}else{
			if(STEP==0)STEP=8;
			STEP--;
		}
		STEP_MOTOR_8A(STEP,speed);
	}
}

void STEP_MOTOR_LOOP (u8 RL,u8 LOOP,u8 speed){//电机按圈数运行
	STEP_MOTOR_NUM(RL,LOOP*4076,speed); 
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"

#include "step_motor.h"

int main (void){//主程序
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	STEP_MOTOR_Init();//步进电机初始化

	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A))STEP_MOTOR_LOOP(0,1,3); // 按圈数右转		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B))STEP_MOTOR_LOOP(1,1,3); //按圈数左转		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C))STEP_MOTOR_NUM(0,100,3); //按步数右转
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D))STEP_MOTOR_NUM(1,100,3); //按步数左转
		else STEP_MOTOR_OFF();//当没有按键时步进电机断电
	}
}

六:RS232串口

本质上是USART串口,将TTL电平转化为RS232电平,这样有更远的传输距离,可以抗干扰能力更强

对开发板跳线进行设置

第一步:将RS232跳线的两个跳线帽处在短接状态

第二步:将RS485,将这组的三个跳线帽断开(占用了PB10 PB11两个端口,必选端口否则产生通讯错误)

第三步:将MY1680,最上方的两个跳线断开(占用了PB10 PB11两个端口,必选端口否则产生通讯错误)

1.RS232串口通信测试程序

接入RS232通讯线

 将数据线的RST和TST短接,让串口发出的数据直接传回到串口内部

测试收发状态:按A键 屏幕上发出数据TX:A 串口接收的数据RX:A;B C D同理

 

 

不论交叉还是直连线,都必须是RXD(接收)连TXD(发送)

 

 新建文件夹

Hardware文件夹——>OLED0561文件夹
              ——>TOUCH_KEY文件夹——>touch_key.c  touch_key.h


Basic文件夹——>usart文件夹       
           ——>ic文件夹            

touch_key.h

#ifndef __TOUCH_KEY_H
#define __TOUCH_KEY_H	 
#include "sys.h"

#define TOUCH_KEYPORT	GPIOA	//定义IO接口组
#define TOUCH_KEY_A		GPIO_Pin_0	//定义IO接口
#define TOUCH_KEY_B		GPIO_Pin_1	//定义IO接口
#define TOUCH_KEY_C		GPIO_Pin_2	//定义IO接口
#define TOUCH_KEY_D		GPIO_Pin_3	//定义IO接口


void TOUCH_KEY_Init(void);//初始化

		 				    
#endif

touch_key.c

#include "touch_key.h"

void TOUCH_KEY_Init(void){ //触摸按键初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | 
                           RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE); //APB2外设GPIO时钟使能      
    GPIO_InitStructure.GPIO_Pin = TOUCH_KEY_A | TOUCH_KEY_B | TOUCH_KEY_C | TOUCH_KEY_D; //选择端口                       
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(TOUCH_KEYPORT,&GPIO_InitStructure);			
}

usart.h

#ifndef __USART_H
#define __USART_H
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "stdio.h"	
#include "sys.h" 


#define USART_n		USART1  //定义使用printf函数的串口,其他串口要使用USART_printf专用函数发送

#define USART1_REC_LEN  			200  	//定义USART1最大接收字节数
#define USART2_REC_LEN  			200  	//定义USART2最大接收字节数
#define USART3_REC_LEN  			200  	//定义USART3最大接收字节数

//不使用某个串口时要禁止此串口,以减少编译量
#define EN_USART1 			1		//使能(1)/禁止(0)串口1
#define EN_USART2 			0		//使能(1)/禁止(0)串口2
#define EN_USART3 			1		//使能(1)/禁止(0)串口3
	  	
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u8  USART2_RX_BUF[USART2_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8  USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
 
extern u16 USART1_RX_STA;         		//接收状态标记	
extern u16 USART2_RX_STA;         		//接收状态标记	
extern u16 USART3_RX_STA;         		//接收状态标记	

//函数声明
void USART1_Init(u32 bound);//串口1初始化并启动
void USART2_Init(u32 bound);//串口2初始化并启动
void USART3_Init(u32 bound);//串口3初始化并启动
void USART1_printf(char* fmt,...); //串口1的专用printf函数
void USART2_printf(char* fmt,...); //串口2的专用printf函数
void USART3_printf(char* fmt,...); //串口3的专用printf函数

#endif

usart.c

#if EN_USART3   //如果使能了接收
u8 USART3_RX_BUF[USART3_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART3_RX_STA=0;       //接收状态标记	  

/*
USART3专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART3_printf("123"); //向USART3发送字符123
*/
void USART3_printf (char *fmt, ...){ 
	char buffer[USART3_REC_LEN+1];  // 数据长度
	u8 i = 0;	
	va_list arg_ptr;
	va_start(arg_ptr, fmt);  
	vsnprintf(buffer, USART3_REC_LEN+1, fmt, arg_ptr);
	while ((i < USART3_REC_LEN) && (i < strlen(buffer))){
        USART_SendData(USART3, (u8) buffer[i++]);
        while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); 
	}
	va_end(arg_ptr);
}

void USART3_Init(u32 BaudRate){ //USART3初始化并启动
   GPIO_InitTypeDef GPIO_InitStructure;
   USART_InitTypeDef USART_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure; 

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能UART3所在GPIOB的时钟
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能串口的RCC时钟

   //串口使用的GPIO口配置
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//设置USART3的RX接口是PB11
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//接口模式 浮空输入
   GPIO_Init(GPIOB, &GPIO_InitStructure);

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//设置USART3的TX接口是PB10
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度50MHz
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//接口模式 复用推挽输出
   GPIO_Init(GPIOB, &GPIO_InitStructure);

   //配置串口
   USART_InitStructure.USART_BaudRate = BaudRate;
   USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
   USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
   USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

   USART_Init(USART3, &USART_InitStructure);//配置串口3
   USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);//使能串口接收中断  
   //USART_ITConfig(USART3, USART_IT_TXE, ENABLE);//串口发送中断在发送数据时开启
   USART_Cmd(USART3, ENABLE);//使能串口3

   //串口中断配置
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
   NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;//允许USART3中断
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//中断等级
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}

//串口3中断服务程序(固定的函数名不能修改)
void USART3_IRQHandler(void){ 	

}
#endif	

 main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "usart.h"


int main (void){//主程序
	u8 a;
	delay_ms(100); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"  RS232 TEST "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6,"TX:    RX:   "); //显示字符串

	USART3_Init(115200);//串口3初始化并启动

	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){USART3_printf("%c",'A');OLED_DISPLAY_8x16(6,4*8,'A');} //向RS232串口发送字符并在OLED上显示		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){USART3_printf("%c",'B');OLED_DISPLAY_8x16(6,4*8,'B');} //向RS232串口发送字符并在OLED上显示		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){USART3_printf("%c",'C');OLED_DISPLAY_8x16(6,4*8,'C');} //向RS232串口发送字符并在OLED上显示
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){USART3_printf("%c",'D');OLED_DISPLAY_8x16(6,4*8,'D');} //向RS232串口发送字符并在OLED上显示

		//查询方式接收
		if(USART_GetFlagStatus(USART3,USART_FLAG_RXNE) != RESET){  //查询串口待处理标志位
			a =USART_ReceiveData(USART3);//读取接收到的数据
			OLED_DISPLAY_8x16(6,11*8,a);//在OLED上显示
		}
	}
}

2.RS485串口通信测试程序

相对于RS232总线来说,具有更高的可靠性,更远的传输距离

对开发板跳线进行设置

第一步:将RS485跳线的三个跳线帽处在短接状态

第二步:将RS232跳线的二个跳线帽全部断开

第三步:将MY1680,最上方的两个跳线断开(与RS485总线复用,必选端口否则产生通讯错误)

需要两个开发板才能看到效果 

 

 

 新建文件夹

Hardware文件夹——>RS485文件夹——>rs485.c rs485.h

usart.h

#include <stdlib.h>
#include <string.h>
#include "stdio.h"	
#include "sys.h" 


#define USART_n		USART1  //定义使用printf函数的串口,其他串口要使用USART_printf专用函数发送

#define USART1_REC_LEN  			200  	//定义USART1最大接收字节数
#define USART2_REC_LEN  			200  	//定义USART2最大接收字节数
#define USART3_REC_LEN  			200  	//定义USART3最大接收字节数

//不使用某个串口时要禁止此串口,以减少编译量
#define EN_USART1 			1		//使能(1)/禁止(0)串口1
#define EN_USART2 			0		//使能(1)/禁止(0)串口2
#define EN_USART3 			1		//使能(1)/禁止(0)串口3
	  	
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u8  USART2_RX_BUF[USART2_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8  USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
 
extern u16 USART1_RX_STA;         		//接收状态标记	
extern u16 USART2_RX_STA;         		//接收状态标记	
extern u16 USART3_RX_STA;         		//接收状态标记	

//函数声明
void USART1_Init(u32 bound);//串口1初始化并启动
void USART2_Init(u32 bound);//串口2初始化并启动
void USART3_Init(u32 bound);//串口3初始化并启动
void USART1_printf(char* fmt,...); //串口1的专用printf函数
void USART2_printf(char* fmt,...); //串口2的专用printf函数
void USART3_printf(char* fmt,...); //串口3的专用printf函数

#endif

rs485.h

#ifndef __RS485_H
#define __RS485_H	 
#include "sys.h"

#define RS485PORT	GPIOA	    //定义IO接口
#define RS485_RE	GPIO_Pin_8	//定义IO接口    切换数据的收发



void RS485_Init(void);                //初始化
void RS485_printf (char *fmt, ...);   //RS485发送    使用USART3的接收程序(方法和RS232相同)
		 				    
#endif

rs485.c

#include "sys.h"
#include "usart.h"
#include "rs485.h"

void RS485_Init(void){ //RS485接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    GPIO_InitStructure.GPIO_Pin = RS485_RE; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //选择IO接口工作方式       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(RS485PORT, &GPIO_InitStructure);
	GPIO_ResetBits(RS485PORT,RS485_RE); //RE端控制接收/发送状态,RE为1时发送,为0时接收。
	
}
 
/*
RS485总线通信,使用USART3,这是RS485专用的printf函数
调用方法:RS485_printf("123"); //向USART3发送字符123
*/
void RS485_printf (char *fmt, ...){ 
	char buffer[USART3_REC_LEN+1];  // 数据长度
	u8 i = 0;
	va_list arg_ptr;
	GPIO_SetBits(RS485PORT,RS485_RE); //为高电平(发送)//RS485收发选择线	
	va_start(arg_ptr, fmt);  
	vsnprintf(buffer, USART3_REC_LEN+1, fmt, arg_ptr);
	while ((i < USART3_REC_LEN) && (i < strlen(buffer))){
        USART_SendData(USART3, (u8) buffer[i++]);
        while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); 
	}
	va_end(arg_ptr);
	GPIO_ResetBits(RS485PORT,RS485_RE); //为低电平(接收)//RS485收发选择线	
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "usart.h"
#include "rs485.h"

int main (void){//主程序
	u8 a;
	delay_ms(100); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"  RS485 TEST "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6,"TX:    RX:   "); //显示字符串

	USART3_Init(115200);//串口3初始化并启动
	RS485_Init();//RS485总线初始化,需要跟在USART3初始化下方

	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){RS485_printf("%c",'A');OLED_DISPLAY_8x16(6,4*8,'A');} //向RS232串口发送字符并在OLED上显示		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){RS485_printf("%c",'B');OLED_DISPLAY_8x16(6,4*8,'B');} //向RS232串口发送字符并在OLED上显示		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){RS485_printf("%c",'C');OLED_DISPLAY_8x16(6,4*8,'C');} //向RS232串口发送字符并在OLED上显示
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){RS485_printf("%c",'D');OLED_DISPLAY_8x16(6,4*8,'D');} //向RS232串口发送字符并在OLED上显示

		//查询方式接收
		if(USART_GetFlagStatus(USART3,USART_FLAG_RXNE) != RESET){  //查询串口待处理标志位
			a =USART_ReceiveData(USART3);//读取接收到的数据
			OLED_DISPLAY_8x16(6,11*8,a);//在OLED上显示
		}
	}
}

七:CAN总线  

1.理论原理分析 

CAN是Controller Area Network的缩写(以下称为CAN) ,是ISO国际标准化的串行通信协议

    CAN协议经过ISO标准化后有两个标准:ISO11898标准和ISO11519-2标准
        ISO11898:是针对通信速率为125Kbps~1Mbps的高速通信标准
        ISO11519-2:是针对通信速率为125Kbps以下的低速通信标准

    应用于:汽车电子、工业自动化、船舶、医疗设备、工业设备等方面

    优点:很高的可靠性、多主控制、系统柔软性、速度快、距离远
          具有较强的纠错能力、故障封闭功能、连接节点多

    只有2个设备:简单通信当成USART串口使用
    多个设备:CAN协议、使用邮箱、识别符、过滤器功能

    数据:所有设备都会接收报文,但标识符(标识符是报文中的一部分)不符的报文会被过滤器删除

    CAN总线发送
        报文:CAN设备一次发送出去的完整数据信息
        邮箱:用于发送报文的发送调度器
        帧种类:不同用途的报文种类。有数据帧、遥控帧、错误帧、过载帧、帧间隔
        帧格式:一个报文里包含的内容
        标识符(标识符列表模式、标识符屏蔽模式):CAN总线上的设备可以用此判断数据是不是发给自己的
    CAN总线的接收
        接收邮箱FIFO:表面的意思是“先入先出”,是指有层级深度的接收邮箱
                     STM32F103系列单片机上有2个FIFO邮箱,
                     每个FIFO有3层深度,与过滤器匹配的报文会被放入FIFO邮箱
        过滤器组
            过滤器:可由硬件判断报文中的标识符,过滤掉标识符不匹配的报文
                   STM32F103系列单片机中的CAN总线控制器提供了14个过滤器组
                   过滤器是由硬件实现的,只有与过滤器匹配的报文才需要软件处理
            过滤器优先级:
                1,位宽为32位的过滤器,优先级高于位宽为16位的过滤器个
                2,对于位宽相同的过滤器,标识符列表模式的优先级高于屏蔽位模式
                3,位宽和模式都相同的过滤器,优先级由过滤器号决定,过滤器号小的优先级高
    其他功能
        工作模式:正常,睡眠,测试
        测试模式中包括:静默、环回、环回静默
        时间触发通信模式
        寄存器访问保护
        中断
        记录接收SOF时刻的时间戳




波特率
    CAN总线是基于相同波特率通信的,所以设备接入前要知道总线上的波特率是多少
    波特率=(pclk1 /((1+8+7)*9))= 36Mhz/16/9= 250Kbits


数据数量
    一次最多只能发送8个字节的数据,这是由CAN协议规定的
    多于8个的需要第二次再发送,或者做一个上层的连续多数据发送的函数

         

 

 

1.1 CAN总线的发送 

报文:CAN设备一次发送出去的完整数据信息

每种报文的格式都不相同
数据帧和遥控帧有两种格式:标准格式 和 扩展格式
                         标准和扩展模式类似于I2C协议里的7位和10位地址

邮箱:用于发送报文的发送调度器

帧种类:不同用途的报文种类。有数据帧、遥控帧、错误帧、过载帧、帧间隔

帧格式:一个报文里包含的内容

 数据帧、遥控帧报文的格式

 标准格式和扩展格式

 

 

标识符(标识符列表模式、标识符屏蔽模式):CAN总线上的设备可以用此判断数据是不是发给自己的

1.2 CAN总线的接收

接收邮箱FIFO

 

过滤器

 

 

2.CAN通信测试程序 

对开发板跳线进行设置

第一步:将CAN总线跳线的两个跳线帽处在短接状态

第二步:将数码管的两个跳线断开(它使用的引脚和CAN复用,断开才不会产生干扰)

 

 

 新建文件夹

Hardware文件夹——>CAN文件夹——>can.c can.h

can.h

#ifndef __CAN_H
#define __CAN_H	 
#include "sys.h"


#define CAN_INT_ENABLE	0	//1 开接收中断,0 关接收中断

//设置模式和波特率
//波特率=(pclk1/((1+8+7)*9)) = 36Mhz/16/9 = 250Kbits设定了一个时间单位的长度9
#define tsjw	CAN_SJW_1tq	//设置项目(1~4)
#define tbs1	CAN_BS1_8tq	//设置项目(1~16)
#define tbs2	CAN_BS2_7tq	//设置项目(1~8)
#define brp		9	//设置项目



u8 CAN1_Configuration(void);//初始化
u8 CAN_Send_Msg(u8* msg,u8 len);//发送数据
u8 CAN_Receive_Msg(u8 *buf);//接收数据
		 				    
#endif

can.c

#include "can.h"



u8 CAN1_Configuration(void){ //CAN初始化(返回0表示设置成功,返回其他表示失败)
    GPIO_InitTypeDef        GPIO_InitStructure; 
    CAN_InitTypeDef         CAN_InitStructure;
    CAN_FilterInitTypeDef   CAN_FilterInitStructure;

#if CAN_INT_ENABLE
    NVIC_InitTypeDef        NVIC_InitStructure;	 
#endif
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA时钟                                                                
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);    //使能CAN1时钟  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
	GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
    //CAN单元设置
    CAN_InitStructure.CAN_TTCM=DISABLE;         //非时间触发通信模式  
    CAN_InitStructure.CAN_ABOM=DISABLE;         //软件自动离线管理   
    CAN_InitStructure.CAN_AWUM=DISABLE;         //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
    CAN_InitStructure.CAN_NART=ENABLE;          //禁止报文自动传送 
    CAN_InitStructure.CAN_RFLM=DISABLE;         //报文不锁定,新的覆盖旧的  
    CAN_InitStructure.CAN_TXFP=DISABLE;         //优先级由报文标识符决定 
    CAN_InitStructure.CAN_Mode= CAN_Mode_Normal;  //模式设置:CAN_Mode_Normal 普通模式,CAN_Mode_LoopBack 回环模式; 
    //设置波特率
    CAN_InitStructure.CAN_SJW=tsjw;             //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位  CAN_SJW_1tq    CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
    CAN_InitStructure.CAN_BS1=tbs1;             //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~ CAN_BS1_16tq
    CAN_InitStructure.CAN_BS2=tbs2;             //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq
    CAN_InitStructure.CAN_Prescaler=brp;        //分频系数(Fdiv)为brp+1  
    CAN_Init(CAN1, &CAN_InitStructure);         //初始化CAN1 
	//设置过滤器
    CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //屏蔽位模式
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //32位宽 
    CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;    //32位ID
    CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器0
    CAN_FilterInit(&CAN_FilterInitStructure);           //滤波器初始化

#if CAN_INT_ENABLE 	//以下是用于CAN中断方式接收的设置
    CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);              //FIFO0消息挂号中断允许.            
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // 主优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            // 次优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
#endif
    return 0;		
}
 
//CAN发送一组数据(固定格式:ID为0X12,标准帧,数据帧)   
//msg:数据指针,最大为8个字节,len:数据长度(最大为8) 
//返回值:0,成功; 其他,失败;
u8 CAN_Send_Msg(u8* msg,u8 len){   
    u8 mbox;
    u16 i=0;
    CanTxMsg TxMessage;
    TxMessage.StdId=0x12;           // 标准标识符 
    TxMessage.ExtId=0x00;           // 设置扩展标识符
    TxMessage.IDE=CAN_Id_Standard;  // 标准帧    就不会使用扩展标识符
    TxMessage.RTR=CAN_RTR_Data;     // 数据帧
    TxMessage.DLC=len;              // 要发送的数据长度
    for(i=0;i<len;i++)
    TxMessage.Data[i]=msg[i];       //写入数据              
    mbox= CAN_Transmit(CAN1,&TxMessage);   
    i=0; 
    while((CAN_TransmitStatus(CAN1,mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束
    if(i>=0XFFF)return 1;
    return 0;    
}

//can口接收数据查询
//buf:数据缓存区;     
//返回值:0,无数据被收到,其他,接收的数据长度;
u8 CAN_Receive_Msg(u8 *buf){                  
    u32 i;
    CanRxMsg RxMessage;
    if(CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;//没有接收到数据,直接退出 
    CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);//读取数据 
    for(i=0;i<8;i++) //把8个数据放入参数数组
    buf[i]=RxMessage.Data[i];  
    return RxMessage.DLC;  //返回数据数量 
}

//CAN的中断接收程序(中断处理程序)	
//必须在can.h文件里CAN_INT_ENABLE为1才能使用中断
//数据处理尽量在中断函数内完成,外部处理要在处理前关CAN中断,防止数据覆盖
void USB_LP_CAN1_RX0_IRQHandler(void){
    CanRxMsg RxMessage;
	vu8 CAN_ReceiveBuff[8]; //CAN总线中断接受的数据寄存器
    vu8 i = 0;
    vu8 u8_RxLen = 0;
  	CAN_ReceiveBuff[0] = 0;	//清空寄存器
    RxMessage.StdId = 0x00;
    RxMessage.ExtId = 0x00;
    RxMessage.IDE = 0;
    RxMessage.RTR = 0;
    RxMessage.DLC = 0;
    RxMessage.FMI = 0;
    for(i=0;i<8;i++){
        RxMessage.Data[i]=0x00; 
    } 
    CAN_Receive(CAN1,CAN_FIFO0,&RxMessage); //读出FIFO0数据
    u8_RxLen = RxMessage.DLC; //读出数据数量
    if(RxMessage.StdId==0x12){//判断ID是否一致  
		CAN_ReceiveBuff[0] = RxMessage.DLC; //将收到数据数量放到数组0的位置
        for( i=0;i<u8_RxLen; i++){ //将收到的数据存入CAN寄存器
            CAN_ReceiveBuff[i] = RxMessage.Data[i]; //将8位数据存入CAN接收寄存器
        }
    }
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "can.h"

int main (void){//主程序
	u8 buff[8];
	u8 x;
	delay_ms(100); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	CAN1_Configuration(); //CAN总线初始化 返回0表示成功

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"   CAN TEST  "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6,"TX:    RX:   "); //显示字符串


	while(1){
		if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)){buff[0]='A';CAN_Send_Msg(buff,1);OLED_DISPLAY_8x16(6,4*8,'A');} //向RS232串口发送字符并在OLED上显示		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)){buff[0]='B';CAN_Send_Msg(buff,1);OLED_DISPLAY_8x16(6,4*8,'B');} //向RS232串口发送字符并在OLED上显示		
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)){buff[0]='C';CAN_Send_Msg(buff,1);OLED_DISPLAY_8x16(6,4*8,'C');} //向RS232串口发送字符并在OLED上显示
		else if(!GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)){buff[0]='D';CAN_Send_Msg(buff,1);OLED_DISPLAY_8x16(6,4*8,'D');} //向RS232串口发送字符并在OLED上显示

		//CAN查寻方式的接收处理
		x = CAN_Receive_Msg(buff); //检查是否收到数据
		if(x){ //判断接收数据的数量,不为0表示有收到数据
			OLED_DISPLAY_8x16(6,11*8,buff[0]);//在OLED上显示
		}
	}
}

八:ADC模数转换器

模拟量的线性电压值变化,转化为数值量的数据:从而实现对光敏电阻热敏电阻等线性电压变化的传感器进行读取


ADC基本特性
    √STM32F103单片机有2个模数转换器——ADC
    √每个ADC为12位分辨率(读出数据的位长度)
    √2个ADC共用16个外部通道(单片机的ADC输入引脚)
    √2个ADC都可使用DMA进行操作

对跳线进行设置

ADC输入的两个跳线短接

1.光敏电阻ADC读取程序(单个通道)

光线变暗数值就会变大

 光线强,光敏电阻阻止变小;上面的电阻使中间点的电压变低;使得ADC读取的数据变小

 新建文件夹

Basic文件夹——>adc文件夹——>adc.c  adc.h

adc.h

#ifndef __ADC_H
#define __ADC_H 			   
#include "sys.h"


#define ADC1_DR_Address    ((uint32_t)0x4001244C) //ADC1这个外设的地址(查参考手册得出)

#define ADCPORT		GPIOA	//定义ADC接口
#define ADC_CH4		GPIO_Pin_4	//定义ADC接口 电压电位器
#define ADC_CH5		GPIO_Pin_5	//定义ADC接口 光敏电阻
#define ADC_CH6		GPIO_Pin_6	//定义ADC接口 摇杆X轴
#define ADC_CH7		GPIO_Pin_7	//定义ADC接口 摇杆Y轴


void ADC_DMA_Init(void);
void ADC_GPIO_Init(void);
void ADC_Configuration(void);

#endif

adc.c

#include "adc.h"

vu16 ADC_DMA_IN5; //ADC数值存放的变量

void ADC_DMA_Init(void){ //DMA初始化设置
	DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体
	DMA_DeInit(DMA1_Channel1);//复位DMA通道1
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_Address
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN5; //定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址
	DMA_InitStructure.DMA_BufferSize = 1;//定义DMA缓冲区大小(根据ADC采集通道数量修改)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;//当前存储器地址:Disable不变,Enable递增(用于多通道采集)
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1
	DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}
void ADC_GPIO_Init(void){ //GPIO初始化设置
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟(用于ADC的数据传送)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC1时钟
    GPIO_InitStructure.GPIO_Pin = ADC_CH5; //选择端口                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //选择IO接口工作方式       
	GPIO_Init(ADCPORT, &GPIO_InitStructure);			
}
void ADC_Configuration(void){ //初始化设置
	ADC_InitTypeDef ADC_InitStructure;//定义ADC初始化结构体变量
	ADC_GPIO_Init();//GPIO初始化设置
	ADC_DMA_Init();//DMA初始化设置
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有软件控制转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC通道的数目(根据ADC采集通道数量修改)
	ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC1,ADC通道x,规则采样顺序值为y,采样时间为28周期		 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_28Cycles5);//ADC1选择信道x,采样顺序y,采样时间n个周期

	ADC_DMACmd(ADC1, ENABLE);// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
	ADC_Cmd(ADC1, ENABLE);//使能ADC1
	ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成
	ADC_StartCalibration(ADC1);//开始ADC1校准
	while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件开始转换
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "adc.h"

extern vu16 ADC_DMA_IN5; //声明外部变量

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	ADC_Configuration(); //ADC初始化设置

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"   ADC TEST  "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6," ADC_IN5:    "); //显示字符串


	while(1){
		//将光敏电阻的ADC数据显示在OLED上
		OLED_DISPLAY_8x16(6,10*8,ADC_DMA_IN5/1000+0x30);//
		OLED_DISPLAY_8x16(6,11*8,ADC_DMA_IN5%1000/100+0x30);//
		OLED_DISPLAY_8x16(6,12*8,ADC_DMA_IN5%100/10+0x30);//
		OLED_DISPLAY_8x16(6,13*8,ADC_DMA_IN5%10+0x30);//
		delay_ms(500); //延时
	}
}

2.光敏和电位器ADC读取程序(两个通道)

adc.h

#ifndef __ADC_H
#define __ADC_H 			   
#include "sys.h"


#define ADC1_DR_Address    ((uint32_t)0x4001244C) //ADC1这个外设的地址(查参考手册得出)

#define ADCPORT		GPIOA	//定义ADC接口
#define ADC_CH4		GPIO_Pin_4	//定义ADC接口 电压电位器
#define ADC_CH5		GPIO_Pin_5	//定义ADC接口 光敏电阻
#define ADC_CH6		GPIO_Pin_6	//定义ADC接口 摇杆X轴
#define ADC_CH7		GPIO_Pin_7	//定义ADC接口 摇杆Y轴


void ADC_DMA_Init(void);
void ADC_GPIO_Init(void);
void ADC_Configuration(void);

#endif

adc.c

#include "adc.h"

vu16 ADC_DMA_IN[2]; //ADC数值存放的变量

void ADC_DMA_Init(void){ //DMA初始化设置
	DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体
	DMA_DeInit(DMA1_Channel1);//复位DMA通道1
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_Address
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址
	DMA_InitStructure.DMA_BufferSize = 2;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 当前存储器地址:Disable不变,Enable递增(用于多通道采集)
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1
	DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}
void ADC_GPIO_Init(void){ //GPIO初始化设置
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟(用于ADC的数据传送)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC1时钟
    GPIO_InitStructure.GPIO_Pin = ADC_CH4 | ADC_CH5; //!!!选择端口                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //选择IO接口工作方式       
	GPIO_Init(ADCPORT, &GPIO_InitStructure);			
}
void ADC_Configuration(void){ //初始化设置
	ADC_InitTypeDef ADC_InitStructure;//定义ADC初始化结构体变量
	ADC_GPIO_Init();//GPIO初始化设置
	ADC_DMA_Init();//DMA初始化设置
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有软件控制转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 2;//!!!顺序进行规则转换的ADC通道的数目(根据ADC采集通道数量修改)
	ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC1,ADC通道x,规则采样顺序值为y,采样时间为28周期		 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期

	ADC_DMACmd(ADC1, ENABLE);// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
	ADC_Cmd(ADC1, ENABLE);//使能ADC1
	ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成
	ADC_StartCalibration(ADC1);//开始ADC1校准
	while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件开始转换
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "adc.h"

extern vu16 ADC_DMA_IN[2]; //声明外部变量

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	ADC_Configuration(); //ADC初始化设置

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"   ADC TEST  "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(4," ADC_IN4:    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6," ADC_IN5:    "); //显示字符串


	while(1){
		//将光敏电阻的ADC数据显示在OLED上
		OLED_DISPLAY_8x16(4,10*8,ADC_DMA_IN[0]/1000+0x30);//
		OLED_DISPLAY_8x16(4,11*8,ADC_DMA_IN[0]%1000/100+0x30);//
		OLED_DISPLAY_8x16(4,12*8,ADC_DMA_IN[0]%100/10+0x30);//
		OLED_DISPLAY_8x16(4,13*8,ADC_DMA_IN[0]%10+0x30);//

		OLED_DISPLAY_8x16(6,10*8,ADC_DMA_IN[1]/1000+0x30);//
		OLED_DISPLAY_8x16(6,11*8,ADC_DMA_IN[1]%1000/100+0x30);//
		OLED_DISPLAY_8x16(6,12*8,ADC_DMA_IN[1]%100/10+0x30);//
		OLED_DISPLAY_8x16(6,13*8,ADC_DMA_IN[1]%10+0x30);//

		delay_ms(500); //延时
	}
}

3.模拟摇杆的ADC读取程序

对开发板的跳线进行设置

第一步 模拟摇杆:上面的三个跳线帽短接

第二步 旋转编码器:上面的三个跳线帽断开(因为与模拟摇杆共用了一组端口)

 模拟摇杆变化:上下推动导致Y轴变化ADC_IN7变化;左右推动导致X轴变化ADC_IN6变化;按下出现”Y“的字样 松开消失

新建文件夹

Hardware文件夹——>JoyStick文件夹——>JoyStick.c  JoyStick.h

adc.h

#ifndef __ADC_H
#define __ADC_H 			   
#include "sys.h"


#define ADC1_DR_Address    ((uint32_t)0x4001244C) //ADC1这个外设的地址(查参考手册得出)

#define ADCPORT		GPIOA	//定义ADC接口
#define ADC_CH4		GPIO_Pin_4	//定义ADC接口 电压电位器
#define ADC_CH5		GPIO_Pin_5	//定义ADC接口 光敏电阻

#define ADC_CH6		GPIO_Pin_6	//定义ADC接口 摇杆X轴
#define ADC_CH7		GPIO_Pin_7	//定义ADC接口 摇杆Y轴


void ADC_DMA_Init(void);
void ADC_GPIO_Init(void);
void ADC_Configuration(void);

#endif

adc.c

#include "adc.h"

vu16 ADC_DMA_IN[2]; //ADC数值存放的变量

void ADC_DMA_Init(void){ //DMA初始化设置
	DMA_InitTypeDef DMA_InitStructure;//定义DMA初始化结构体
	DMA_DeInit(DMA1_Channel1);//复位DMA通道1
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_Address
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_DMA_IN; //!!!定义DMA通道ADC数据存储器(其他函数可直接读此变量即是ADC值)
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设为源地址
	DMA_InitStructure.DMA_BufferSize = 2;//!!!定义DMA缓冲区大小(根据ADC采集通道数量修改)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//当前外设寄存器地址不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//!!! 当前存储器地址:Disable不变,Enable递增(用于多通道采集)
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//定义外设数据宽度16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //定义存储器数据宽度16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA通道操作模式位环形缓冲模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道优先级高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//禁止DMA通道存储器到存储器传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);//初始化DMA通道1
	DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}


void ADC_GPIO_Init(void){ //GPIO初始化设置
	GPIO_InitTypeDef  GPIO_InitStructure; 	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟(用于ADC的数据传送)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//使能ADC1时钟

    GPIO_InitStructure.GPIO_Pin = ADC_CH6 | ADC_CH7; //!!!选择端口    
                    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //选择IO接口工作方式       
	GPIO_Init(ADCPORT, &GPIO_InitStructure);			
}


void ADC_Configuration(void){ //初始化设置
	ADC_InitTypeDef ADC_InitStructure;//定义ADC初始化结构体变量
	ADC_GPIO_Init();//GPIO初始化设置
	ADC_DMA_Init();//DMA初始化设置
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE; //使能扫描
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//ADC转换工作在连续模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//有软件控制转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 2;//!!!顺序进行规则转换的ADC通道的数目(根据ADC采集通道数量修改)
	ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
	//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
	//ADC1,ADC通道x,规则采样顺序值为y,采样时间为28周期		 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 2, ADC_SampleTime_28Cycles5);//!!! ADC1选择信道x,采样顺序y,采样时间n个周期

	ADC_DMACmd(ADC1, ENABLE);// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
	ADC_Cmd(ADC1, ENABLE);//使能ADC1
	ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1));//等待ADC1校准重置完成
	ADC_StartCalibration(ADC1);//开始ADC1校准
	while(ADC_GetCalibrationStatus(ADC1));//等待ADC1校准完成
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC1软件开始转换
}

JoyStick.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"


#define JoyStickPORT	GPIOB	    //定义IO接口组
#define JoyStick_KEY	GPIO_Pin_2	//定义IO接口


void JoyStick_Init(void);//初始化

		 				    
#endif

JoyStick.c

#include "JoyStick.h"

void JoyStick_Init(void){ //微动开关的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       
    GPIO_InitStructure.GPIO_Pin = JoyStick_KEY; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(JoyStickPORT,&GPIO_InitStructure);			
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "adc.h"
#include "JoyStick.h"

extern vu16 ADC_DMA_IN[2]; //声明外部变量

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	ADC_Configuration(); //ADC初始化设置(模拟摇杆的ADC初始化)
	JoyStick_Init(); //模拟摇杆的按键初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"   ADC TEST  "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(4," ADC_IN6:    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(6," ADC_IN7:    "); //显示字符串


	while(1){
		//将光敏电阻的ADC数据显示在OLED上
		OLED_DISPLAY_8x16(4,10*8,ADC_DMA_IN[0]/1000+0x30);//
		OLED_DISPLAY_8x16(4,11*8,ADC_DMA_IN[0]%1000/100+0x30);//
		OLED_DISPLAY_8x16(4,12*8,ADC_DMA_IN[0]%100/10+0x30);//
		OLED_DISPLAY_8x16(4,13*8,ADC_DMA_IN[0]%10+0x30);//

		OLED_DISPLAY_8x16(6,10*8,ADC_DMA_IN[1]/1000+0x30);//
		OLED_DISPLAY_8x16(6,11*8,ADC_DMA_IN[1]%1000/100+0x30);//
		OLED_DISPLAY_8x16(6,12*8,ADC_DMA_IN[1]%100/10+0x30);//
		OLED_DISPLAY_8x16(6,13*8,ADC_DMA_IN[1]%10+0x30);//

		if(GPIO_ReadInputDataBit(JoyStickPORT,JoyStick_KEY)==0){
			OLED_DISPLAY_8x16(0,0,'Y');//
		}else{
			OLED_DISPLAY_8x16(0,0,' ');//
		}
		delay_ms(200); //延时
	}
}

九:MP3播放

 对开发板的跳线进行设置

第一步:旋转编码器的三个跳线帽全部短接

第二步:模拟摇杆的三个跳线帽全部断开

第三步:左边TF CARO下面四个跳线短接;中间上面四个跳线短接;右边1 2 5 6跳线短接


第四步:将TF卡插入到卡槽

第五步:插入与电脑连接的MicroUSB接口(TF卡槽的LED等变亮)


第六步:在电脑会显示一个U盘(要先格式化:FAT格式 FAT32格式)、勾选快速格式化、存入音乐文件(mp3格式 wav格式)(音乐前面必须命名有序号)

第七步:把USB与电脑连接的MicroUSB接口拔出,插回到核心板上面的接口

1.MP3播放测试程序

 

 

新建文件夹

Hardware文件夹——>MY1690文件夹——>MY1690.c  MY1690.h

usart.h

#ifndef __USART_H
#define __USART_H
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "stdio.h"	
#include "sys.h" 


#define USART_n		USART1  //定义使用printf函数的串口,其他串口要使用USART_printf专用函数发送

#define USART1_REC_LEN  			200  	//定义USART1最大接收字节数
#define USART2_REC_LEN  			200  	//定义USART2最大接收字节数
#define USART3_REC_LEN  			200  	//定义USART3最大接收字节数

//不使用某个串口时要禁止此串口,以减少编译量
#define EN_USART1 			1		//使能(1)/禁止(0)串口1
#define EN_USART2 			0		//使能(1)/禁止(0)串口2
#define EN_USART3 			1		//使能(1)/禁止(0)串口3
	  	
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u8  USART2_RX_BUF[USART2_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u8  USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
 
extern u16 USART1_RX_STA;         		//接收状态标记	
extern u16 USART2_RX_STA;         		//接收状态标记	
extern u16 USART3_RX_STA;         		//接收状态标记	

//函数声明
void USART1_Init(u32 bound);//串口1初始化并启动
void USART2_Init(u32 bound);//串口2初始化并启动
void USART3_Init(u32 bound);//串口3初始化并启动
void USART1_printf(char* fmt,...); //串口1的专用printf函数
void USART2_printf(char* fmt,...); //串口2的专用printf函数
void USART3_printf(char* fmt,...); //串口3的专用printf函数

#endif

usart.c

#include "sys.h"
#include "usart.h"
	  	 
//使UASRT串口可用printf函数发送
//在usart.h文件里可更换使用printf函数的串口号	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE {
	int handle; 
}; 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x){ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f){      
	while((USART_n->SR&0X40)==0);//循环发送,直到发送完毕   
    USART_n->DR = (u8) ch;      
	return ch;
}
#endif 


/*
USART1串口相关程序
*/
 
#if EN_USART1   //USART1使用与屏蔽选择
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART1_RX_STA=0;       //接收状态标记	  

/*
USART1专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART1_printf("123"); //向USART2发送字符123
*/
void USART1_printf (char *fmt, ...){ 
	char buffer[USART1_REC_LEN+1];  // 数据长度
	u8 i = 0;	
	va_list arg_ptr;
	va_start(arg_ptr, fmt);  
	vsnprintf(buffer, USART1_REC_LEN+1, fmt, arg_ptr);
	while ((i < USART1_REC_LEN) && (i < strlen(buffer))){
        USART_SendData(USART1, (u8) buffer[i++]);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); 
	}
	va_end(arg_ptr);
}

void USART1_Init(u32 bound){ //串口1初始化并启动
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
     //USART1_TX   PA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);  
    //USART1_RX	  PA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
   //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 
   //USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口
    USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//开启ENABLE/关闭DISABLE中断
    USART_Cmd(USART1, ENABLE);                    //使能串口 
}

void USART1_IRQHandler(void){ //串口1中断服务程序(固定的函数名不能修改)	
	u8 Res;
	//以下是字符串接收到USART_RX_BUF[]的程序,(USART_RX_STA&0x3FFF)是数据的长度(不包括回车)
	//当(USART_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。
	//在主函数里写判断if(USART_RX_STA&0xC000),然后读USART_RX_BUF[]数组,读到0x0d 0x0a即是结束。
	//注意在主函数处理完串口数据后,要将USART_RX_STA清0
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){  //接收中断(接收到的数据必须是0x0d 0x0a结尾)		
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		printf("%c",Res); //把收到的数据以 a符号变量 发送回电脑		
		if((USART1_RX_STA&0x8000)==0){//接收未完成			
			if(USART1_RX_STA&0x4000){//接收到了0x0d				
				if(Res!=0x0a)USART1_RX_STA=0;//接收错误,重新开始
				else USART1_RX_STA|=0x8000;	//接收完成了 
			}else{ //还没收到0X0D					
				if(Res==0x0d)USART1_RX_STA|=0x4000;
				else{
					USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组
					USART1_RX_STA++;	//数据长度计数加1
					if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
} 
#endif	

/*
USART2串口相关程序
*/
#if EN_USART2   //USART2使用与屏蔽选择
u8 USART2_RX_BUF[USART2_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART2_RX_STA=0;       //接收状态标记	  

/*
USART2专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART2_printf("123"); //向USART2发送字符123
*/
void USART2_printf (char *fmt, ...){ 
	char buffer[USART2_REC_LEN+1];  // 数据长度
	u8 i = 0;	
	va_list arg_ptr;
	va_start(arg_ptr, fmt);  
	vsnprintf(buffer, USART2_REC_LEN+1, fmt, arg_ptr);
	while ((i < USART2_REC_LEN) && (i < strlen(buffer))){
        USART_SendData(USART2, (u8) buffer[i++]);
        while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); 
	}
	va_end(arg_ptr);
}


void USART2_Init(u32 bound){ //串口1初始化并启动
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
		 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能UART2所在GPIOA的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口的RCC时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //设置USART2的RX接口是PA3
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure); 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //设置USART2的TX接口是PA2
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);  

   //USART2 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART2, &USART_InitStructure); //初始化串口
    USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);//开启ENABLE/关闭DISABLE中断
    USART_Cmd(USART2, ENABLE);                    //使能串口 
   //Usart2 NVIC 配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 
}

void USART2_IRQHandler(void){ //串口2中断服务程序(固定的函数名不能修改)	

} 
#endif	


#if EN_USART3   //如果使能了接收
u8 USART3_RX_BUF[USART3_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART3_RX_STA=0;       //接收状态标记	  

/*
USART3专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART3_printf("123"); //向USART3发送字符123
*/
void USART3_printf (char *fmt, ...){ 
	char buffer[USART3_REC_LEN+1];  // 数据长度
	u8 i = 0;	
	va_list arg_ptr;
	va_start(arg_ptr, fmt);  
	vsnprintf(buffer, USART3_REC_LEN+1, fmt, arg_ptr);
	while ((i < USART3_REC_LEN) && (i < strlen(buffer))){
        USART_SendData(USART3, (u8) buffer[i++]);
        while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); 
	}
	va_end(arg_ptr);
}

void USART3_Init(u32 BaudRate){ //USART3初始化并启动
   GPIO_InitTypeDef GPIO_InitStructure;
   USART_InitTypeDef USART_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure; 

   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能UART3所在GPIOB的时钟
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能串口的RCC时钟

   //串口使用的GPIO口配置
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//设置USART3的RX接口是PB11
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//接口模式 浮空输入
   GPIO_Init(GPIOB, &GPIO_InitStructure);

   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//设置USART3的TX接口是PB10
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度50MHz
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//接口模式 复用推挽输出
   GPIO_Init(GPIOB, &GPIO_InitStructure);
   //Usart1 NVIC 配置
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 
   //配置串口
   USART_InitStructure.USART_BaudRate = BaudRate;
   USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
   USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
   USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

   USART_Init(USART3, &USART_InitStructure);//配置串口3
   USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//使能串口接收中断  
   USART_Cmd(USART3, ENABLE);//使能串口3

}

//串口3中断服务程序(固定的函数名不能修改)
void USART3_IRQHandler(void){ 	
    u8 Res;
    if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET){  //接收中断
        Res =USART_ReceiveData(USART3);//读取接收到的数据
        if(Res=='S'){//判断数据是否是STOP(省略读取S)            
            USART3_RX_STA=1;//如果是STOP则标志位为1      
        }else if(Res=='K'){//判断数据是否是OK(省略读取K)            
            USART3_RX_STA=2;//如果是OK则标志位为2      
        }            
    }
}
#endif	

MY1690.h

#ifndef __MY1690_H
#define __MY1690_H	 
#include "sys.h"
#include "usart.h"


void MY1690_Init(void);//初始化

void MY1690_PLAY(void);	//直接输入的指令
void MY1690_PREV(void);
void MY1690_NEXT(void);
void MY1690_PAUSE(void);
void MY1690_VUP(void);
void MY1690_VDOWN(void);
void MY1690_STOP(void);

void MY1690_CMD1(u8 a);	//全部指令输入
void MY1690_CMD2(u8 a,u8 b); 
void MY1690_CMD3(u8 a,u16 b);
		 				    
#endif

MY1690.c

#include "MY1690.h"

void MY1690_Init(void){ //初始化
	USART3_Init(9600);//串口3初始化并启动
	MY1690_STOP(); //上电初始化后发送一次指令激活MP3芯片
}
void MY1690_PLAY(void){ //播放
	USART3_printf("\x7e\x03\x11\x12\xef"); //其中 \x 后接十六进制数据
}
void MY1690_PAUSE(void){ //暂停
	USART3_printf("\x7e\x03\x12\x11\xef");
}
void MY1690_PREV(void){ //上一曲
	USART3_printf("\x7e\x03\x14\x17\xef");
}
void MY1690_NEXT(void){ //下一曲
	USART3_printf("\x7e\x03\x13\x10\xef");
}
void MY1690_VUP(void){ //音量加1
	USART3_printf("\x7e\x03\x15\x16\xef");
}
void MY1690_VDOWN(void){ //音量减1
	USART3_printf("\x7e\x03\x16\x15\xef");
}
void MY1690_STOP(void){ //停止
	USART3_printf("\x7e\x03\x1E\x1D\xef");
}

void MY1690_CMD1(u8 a){	//无参数的指令发送  a操作码
	u8 i;
	i=3^a;//取校验码(异或)
	USART_SendData(USART3 , 0x7e);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , 0x03);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , a);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , i);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , 0xef);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
}
void MY1690_CMD2(u8 a,u8 b){ //有参数的指令发送 a操作码 b参数
	u8 i;
	i=4^a;//取校验码(异或)
	i=i^b;//取校验码(异或)
	USART_SendData(USART3 , 0x7e);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , 0x04);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , a);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , b);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , i);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , 0xef);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
}
void MY1690_CMD3(u8 a,u16 b){ //有参数的指令发送 a操作码 b参数(16位)
	u8 i,c,d;
	c=b/0x100; //将16位参数分成2个8位参数
	d=b%0x100;
	i=5^a;//取校验码(异或)
	i=i^c;//取校验码(异或)
	i=i^d;//取校验码(异或)
	USART_SendData(USART3 , 0x7e);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , 0x05);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , a);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , c);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , d);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , i);		while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
	USART_SendData(USART3 , 0xef);	while(USART_GetFlagStatus(USART3, USART_FLAG_TC)==RESET); //检查发送中断标志位
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "encoder.h"
#include "usart.h"
#include "my1690.h"

int main (void){//主程序
	u8 b;
	u8 MP3=0;
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	ENCODER_Init(); //旋转编码器初始化
	MY1690_Init(); //MP3芯片初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(3," MP3 PLAY TEST  "); //显示字符串

	while(1){
		if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)==0    //判断4个按键是否按下
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)==0){
			delay_ms(20); //延时
			if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)==0){	//4个按键:A上一曲,B下一曲,
				MY1690_PREV(); //上一曲
				OLED_DISPLAY_8x16_BUFFER(6,"  -- PREV --    "); //显示字符串
				delay_ms(500); //延时
				OLED_DISPLAY_8x16_BUFFER(6,"  -- PLAY --    "); //显示字符串
			}
			if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)==0){
				MY1690_NEXT(); //下一曲
				OLED_DISPLAY_8x16_BUFFER(6,"  -- NEXT --    "); //显示字符串
				delay_ms(500); //延时
				OLED_DISPLAY_8x16_BUFFER(6,"  -- PLAY --    "); //显示字符串
			}
			if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)==0){
				MY1690_CMD2(0x31,30); //将音量设置为30(最大)
				delay_ms(500); //延时
			}
			if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)==0){
				MY1690_CMD3(0x41,0x04); //直接播放第0004曲
				delay_ms(500); //延时
			}
			while(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)==0	//等待按键放开
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)==0);
		}
		b=ENCODER_READ();	//读出旋转编码器按键值	
		if(b==1){MY1690_VUP();} //右转音量加。
		if(b==2){MY1690_VDOWN();}//左转音量减。
		if(b==3){	//按下播放或暂停
            //判断当前是播放还是暂停
			if(MP3==0){//播放	                
				MP3=1;MY1690_PLAY();
				OLED_DISPLAY_8x16_BUFFER(6,"  -- PLAY --    "); //显示字符串
			}else if(MP3==1){//暂停
				MP3=0;MY1690_PAUSE();
				OLED_DISPLAY_8x16_BUFFER(6,"  -- PAUSE --   "); //显示字符串
			}
			delay_ms(500); //延时
		}
		//串口接收处理
        if(USART3_RX_STA==1){ //如果标志位是1 表示收到STOP
            MP3=0;
            OLED_DISPLAY_8x16_BUFFER(6,"  -- STOP --    "); //显示字符串
            USART3_RX_STA=0; //将串口数据标志位清0
        }else if(USART3_RX_STA==2){    //如果标志位是1 表示收到OK
            //加入相关的处理程序
            USART3_RX_STA=0; //将串口数据标志位清0
        }
	}
}

2.MP3语音播报程序

 新建文件夹

Basic文件夹——>rtc文件夹——>rtc.c  rtc.h

rtc.h

#ifndef __RTC_H
#define __RTC_H	 
#include "sys.h" 


//全局变量的声明,在rtc.c文件中定义
//以下2条是使用extern语句声明全局变量
//注意:这里不能给变量赋值
extern u16 ryear;
extern u8 rmon,rday,rhour,rmin,rsec,rweek;



void RTC_First_Config(void);//首次启用RTC的设置
void RTC_Config(void);//实时时钟初始化
u8 Is_Leap_Year(u16 year);//判断是否是闰年函数                    
u8 RTC_Get(void);//读出当前时间值	
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec);//写入当前时间
u8 RTC_Get_Week(u16 year,u8 month,u8 day);//按年月日计算星期




#endif

rtc.c

/* 
//时间读写与设置说明//
    1,在mani函数开头放入RTC_Config();就可以使能时钟了。
       在RTC_Config();函数中自带判断是不是首次使用RTC
    2,使用 RTC_Get();读出时间。读出的数据存放在:
        年 ryear (16位)
        月 rmon	 (以下都是8位)
        日 rday
        时 rhour
        分 rmin
        秒 rsec
        周 rweek

    3,使用 RTC_Set(4位年,2位月,2位日,2位时,2位分,2位秒); 写入时间。
       例如:RTC_Get(2017,08,06,21,34,00);

    其他函数都是帮助如上3个函数的,不需要调用。 
    注意要使用RTC_Get和RTC_Set的返回值,为0时表示读写正确。

*/


#include "sys.h"
#include "rtc.h"


//以下2条全局变量--用于RTC时间的读取
u16 ryear; //4位年
u8 rmon,rday,rhour,rmin,rsec,rweek;//2位月日时分秒周



void RTC_First_Config(void){ //首次启用RTC的设置
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);//启用PWR和BKP的时钟(from APB1)
    PWR_BackupAccessCmd(ENABLE);//后备域解锁
    BKP_DeInit();//备份寄存器模块复位
    RCC_LSEConfig(RCC_LSE_ON);//外部32.768KHZ晶振开启   
    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待稳定    
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//RTC时钟源配置成LSE(外部低速晶振32.768KHZ)    
    RCC_RTCCLKCmd(ENABLE);//RTC开启    
    RTC_WaitForSynchro();//开启后需要等待APB1时钟与RTC时钟同步,才能读写寄存器    
    RTC_WaitForLastTask();//读写寄存器前,要确定上一个操作已经结束
    RTC_SetPrescaler(32767);//设置RTC分频器,使RTC时钟为1Hz,RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)   
    RTC_WaitForLastTask();//等待寄存器写入完成	
    //当不使用RTC秒中断,可以屏蔽下面2条
//    RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断   
//    RTC_WaitForLastTask();//等待写入完成
}

void RTC_Config(void){ //实时时钟初始化
    //在BKP的后备寄存器1中,存了一个特殊字符0xA5A5
    //第一次上电或后备电源掉电后,该寄存器数据丢失,表明RTC数据丢失,需要重新配置
    if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//判断寄存数据是否丢失       
        RTC_First_Config();//重新配置RTC        
        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
    }else{
		//若后备寄存器没有掉电,则无需重新配置RTC
        //这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型
        if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET){
            //这是上电复位
        }
        else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){
            //这是外部RST管脚复位
        }       
        RCC_ClearFlag();//清除RCC中复位标志

        //虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行
        //但是每次上电后,还是要使能RTCCLK
        RCC_RTCCLKCmd(ENABLE);//使能RTCCLK        
        RTC_WaitForSynchro();//等待RTC时钟与APB1时钟同步

        //当不使用RTC秒中断,可以屏蔽下面2条
//        RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能秒中断        
//        RTC_WaitForLastTask();//等待操作完成
    }
	#ifdef RTCClockOutput_Enable   
	    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
	    PWR_BackupAccessCmd(ENABLE);   
	    BKP_TamperPinCmd(DISABLE);   
	    BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
	#endif
}

void RTC_IRQHandler(void){ //RTC时钟1秒触发中断函数(名称固定不可修改)
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET){

	}
	RTC_ClearITPendingBit(RTC_IT_SEC); 
	RTC_WaitForLastTask();
}

void RTCAlarm_IRQHandler(void){	//闹钟中断处理(启用时必须调高其优先级)
	if(RTC_GetITStatus(RTC_IT_ALR) != RESET){
	
	}
	RTC_ClearITPendingBit(RTC_IT_ALR);
	RTC_WaitForLastTask();
}

//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year){                    
	if(year%4==0){ //必须能被4整除
		if(year%100==0){		
			if(year%400==0)return 1;//如果以00结尾,还要能被400整除          
			else return 0;  
		}else return 1;  
	}else return 0;
}                           
//设置时钟
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份

//月份数据表                                                                       
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表  
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表

//写入时间
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec){ //写入当前时间(1970~2099年有效),
	u16 t;
	u32 seccount=0;
	if(syear<2000||syear>2099)return 1;//syear范围1970-2099,此处设置范围为2000-2099       
	for(t=1970;t<syear;t++){ //把所有年份的秒钟相加
		if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
		else seccount+=31536000;                    //平年的秒钟数
	}
	smon-=1;
	for(t=0;t<smon;t++){         //把前面月份的秒钟数相加
		seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数        
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
	seccount+=(u32)hour*3600;//小时秒钟数
	seccount+=(u32)min*60;      //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去
	RTC_First_Config(); //重新初始化时钟
	BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
	RTC_SetCounter(seccount);//把换算好的计数器值写入
	RTC_WaitForLastTask(); //等待写入完成
	return 0; //返回值:0,成功;其他:错误代码.    
}

//读出时间
u8 RTC_Get(void){//读出当前时间值 //返回值:0,成功;其他:错误代码.
	static u16 daycnt=0;
	u32 timecount=0;
	u32 temp=0;
	u16 temp1=0;
	timecount=RTC_GetCounter();		
	temp=timecount/86400;   //得到天数(秒钟数对应的)
	if(daycnt!=temp){//超过一天了
		daycnt=temp;
		temp1=1970;  //从1970年开始
		while(temp>=365){
		     if(Is_Leap_Year(temp1)){//是闰年
			     if(temp>=366)temp-=366;//闰年的秒钟数
			     else {temp1++;break;} 
		     }
		     else temp-=365;       //平年
		     temp1++; 
		}  
		ryear=temp1;//得到年份
		temp1=0;
		while(temp>=28){//超过了一个月
			if(Is_Leap_Year(ryear)&&temp1==1){//当年是不是闰年/2月份
				if(temp>=29)temp-=29;//闰年的秒钟数
				else break;
			}else{
	            if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
	            else break;
			}
			temp1++; 
		}
		rmon=temp1+1;//得到月份
		rday=temp+1;  //得到日期
	}
	temp=timecount%86400;     //得到秒钟数      
	rhour=temp/3600;     //小时
	rmin=(temp%3600)/60; //分钟     
	rsec=(temp%3600)%60; //秒钟
	rweek=RTC_Get_Week(ryear,rmon,rday);//获取星期  
	return 0;
}    

u8 RTC_Get_Week(u16 year,u8 month,u8 day){ //按年月日计算星期(只允许1901-2099年)//已由RTC_Get调用    
	u16 temp2;
	u8 yearH,yearL;
	yearH=year/100;     
	yearL=year%100;
	// 如果为21世纪,年份数加100 
	if (yearH>19)yearL+=100;
	// 所过闰年数只算1900年之后的 
	temp2=yearL+yearL/4;
	temp2=temp2%7;
	temp2=temp2+day+table_week[month-1];
	if (yearL%4==0&&month<3)temp2--;
	return(temp2%7); //返回星期值
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"
#include "rtc.h"

#include "encoder.h"
#include "usart.h"
#include "my1690.h"

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RTC_Config();//实时时钟初始化
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	ENCODER_Init(); //旋转编码器初始化
	MY1690_Init(); //MP3芯片初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2," MP3 TIME READ  "); //显示字符串

	while(1){

		if(RTC_Get()==0){
			OLED_DISPLAY_8x16(4,8*3,rhour/10+0x30);//显示时间
			OLED_DISPLAY_8x16(4,8*4,rhour%10+0x30);//显示时间    
			OLED_DISPLAY_8x16(4,8*5,':');//
			OLED_DISPLAY_8x16(4,8*6,rmin/10+0x30);//显示时间
			OLED_DISPLAY_8x16(4,8*7,rmin%10+0x30);//显示时间
			OLED_DISPLAY_8x16(4,8*8,':');//
			OLED_DISPLAY_8x16(4,8*9,rsec/10+0x30);//显示时间
			OLED_DISPLAY_8x16(4,8*10,rsec%10+0x30);//显示时间
			delay_ms(200); //延时
		}
		if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)==0    //判断4个按键是否按下
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)==0){
			delay_ms(20); //延时
			if(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)==0){	//4个按键:A上一曲,B下一曲,
				OLED_DISPLAY_8x16_BUFFER(6,"  -- PLAY --    "); //显示字符串
				//无语法    18点:一 十 八点(但是正确应该是十八点)
//				MY1690_CMD3(0x41,12); //直接播放
//				MY1690_CMD3(0x41,rhour/10+1); //直接播放    得到语言文件编号,因为是0开始的(自定义语言包)
//				MY1690_CMD3(0x41,11); //直接播放
//				MY1690_CMD3(0x41,rhour%10+1); //直接播放
//				MY1690_CMD3(0x41,13); //直接播放

//				MY1690_CMD3(0x41,rmin/10+1); //直接播放
//				MY1690_CMD3(0x41,11); //直接播放
//				MY1690_CMD3(0x41,rmin%10+1); //直接播放
//				MY1690_CMD3(0x41,14); //直接播放
//				MY1690_CMD3(0x41,rsec/10+1); //直接播放
//				MY1690_CMD3(0x41,11); //直接播放
//				MY1690_CMD3(0x41,rsec%10+1); //直接播放
//				MY1690_CMD3(0x41,15); //直接播放

				//有语法    组合播放
				MY1690_CMD3(0x41,12);     //直接播放
    //小时
            //小时的十位
				if(rhour/10==0){          //小时值的十位=0,不发音(零 十 八点)
					//不发音
				}else if(rhour/10==1){    //小时值的十位=1,播放(十)
					MY1690_CMD3(0x41,11); //直接播放
				}else{
					MY1690_CMD3(0x41,rhour/10+1); //直接播放    几(二)
					MY1690_CMD3(0x41,11);         //直接播放    播放(十)
				}
            //小时的个位
				if(rhour%10!=0 || rhour==0){      //0读音发音
					MY1690_CMD3(0x41,rhour%10+1); //直接播放
				}
				MY1690_CMD3(0x41,13); //直接播放
    //分钟
				if(rmin/10==0){
					MY1690_CMD3(0x41,rmin/10+1); //直接播放
				}else if(rmin/10==1){
					MY1690_CMD3(0x41,11); //直接播放
				}else{
					MY1690_CMD3(0x41,rmin/10+1); //直接播放
					MY1690_CMD3(0x41,11); //直接播放
				}
				if(rmin%10!=0 || rmin==0){
					MY1690_CMD3(0x41,rmin%10+1); //直接播放
				}
				MY1690_CMD3(0x41,14); //直接播放
    //秒钟
				if(rsec/10==0){
					MY1690_CMD3(0x41,rsec/10+1); //直接播放
				}else if(rsec/10==1){
					MY1690_CMD3(0x41,11); //直接播放
				}else{
					MY1690_CMD3(0x41,rsec/10+1); //直接播放
					MY1690_CMD3(0x41,11); //直接播放
				}
				if(rsec%10!=0 || rsec==0){
					MY1690_CMD3(0x41,rsec%10+1); //直接播放
				}
				MY1690_CMD3(0x41,15); //直接播放
			}
    //等待按键放开
			while(GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_A)==0	
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_B)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_C)==0 
			||GPIO_ReadInputDataBit(TOUCH_KEYPORT,TOUCH_KEY_D)==0);
		}
		//串口接收处理
        if(USART3_RX_STA==1){ //如果标志位是1 表示收到STOP
            OLED_DISPLAY_8x16_BUFFER(6,"  -- STOP --    "); //显示字符串
            USART3_RX_STA=0; //将串口数据标志位清0
        }
	}
}

十:SPI总线

 

第一步:需要自己准备一个U盘(容量不得大于32G)
        要先格式化:(FAT格式 FAT32格式)、勾选快速格式化

第二步:将U盘插入到开发板的U盘插口



对开发板的跳线进行设置:
    左边TF CARD下方四个跳线短接;中间CH376上方四个跳线短接;右边MX16801 2 5 6四个跳线短接

1.CH376文件管理控制芯片 

作用:用于单片机系统读写U盘或者SD卡中的文件


CH376支持三种通讯接口:8位并口、SPI接口或者异步串口


特点
    支持1.5Mbps 低速和12Mbps 全速USB 通讯,兼容USB V2.0,外围元器件只需要晶体和电容
    支持USB-HOST主机接口和USB-DEVICE设备接口,支持动态切换主机方式与设备方式
    支持USB设备的控制传输、批量传输、中断传输
    自动检测USB设备的连接和断开,提供设备连接和断开的事件通知
    提供6MHz的SPI主机接口,支持SD卡以及与其协议兼容的MMC卡和TF卡等
    内置USB控制传输的协议处理器,简化常用的控制传输
    内置固件处理海量存储设备的专用通讯协议
        支持Bulk-Only传输协议和SCSI、UFI、RBC 或等效命令集的USB存储设备(包括U盘/USB硬盘/USB闪存盘/USB读卡器)
    内置FAT16和FAT32以及FAT12文件系统的管理固件,支持容量高达32GB 的U盘和SD卡
    提供文件管理功能:打开、新建或删除文件、枚举和搜索文件、创建子目录、支持长文件名
    提供文件读写功能:以字节为最小单位或者以扇区为单位对多级子目录下的文件进行读写
    提供磁盘管理功能:初始化磁盘、查询物理容量、查询剩余空间、物理扇区读写
    提供2MB速度的8位被动并行接口,支持连接到单片机的并行数据总线
    提供2MB/24MHz速度的SPI设备接口,支持连接到单片机的SPI串行总线
    提供最高3Mbps 速度的异步串口,支持连接到单片机的串行口,支持通讯波特率动态调整
    支持5V电源电压和3.3V电源电压以及3V电源电压,支持低功耗模式
    USB设备方式完全兼容CH372芯片;USB主机方式基本兼容CH375芯片
    提供SOP-28和 SSOP20无铅封装,兼容RoHS,提供 SOP28到DIP28的转换板,SOP28封装的引脚基本兼容CH375芯片


封装:CH376S封装、CH376T封装

2.U盘插拔测试程序

 

 

新建文件夹

Hardware文件夹——>CH376文件夹——>ch376.c ch376.h  ch376inc.h  filesys.c filesys.h


Basic文件夹——>spi文件夹——>spi.c spi.h

spi.h

#ifndef __SPI_H
#define __SPI_H	 
#include "sys.h"

#define SPI2PORT		GPIOB	//定义IO接口
#define SPI2_MOSI		GPIO_Pin_15	//定义IO接口
#define SPI2_MISO		GPIO_Pin_14	//定义IO接口
#define SPI2_SCK		GPIO_Pin_13	//定义IO接口
#define SPI2_NSS		GPIO_Pin_12	//定义IO接口

void SPI2_Init(void);
u8 SPI2_SendByte(u8 Byte);
		 				    
#endif

spi.c

#include "spi.h"


void SPI2_Init(void){ //SPI2初始化
	SPI_InitTypeDef  SPI_InitStructure;
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//使能SPI_2时钟

	GPIO_InitStructure.GPIO_Pin = SPI2_MISO;  //SPI2的MISO(PB14)为浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(SPI2PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = SPI2_MOSI | SPI2_SCK;	//SPI2的MOSI(PB15)和SCLK(PB13)为复用推免输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(SPI2PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = SPI2_NSS;	 //SPI2的NSS(PB12)为推免输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SPI2PORT,&GPIO_InitStructure);

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线输入输出全双工模式
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置为SPI的主机模式(SCK主动产生时钟)
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//SPI数据大小:发送8位帧数据结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//空闲状态时SCK的状态,High为高电平,Low为低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//时钟相位,1表示在SCK的奇数沿边采样,2表示偶数沿边采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS由软件控件片选
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//时钟的预分频值(0~256)
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //MSB高位在前
	SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC较验和的多项式
	SPI_Init(SPI2,&SPI_InitStructure); //初始化SPI2的配置项
	SPI_Cmd(SPI2,ENABLE); //使能SPI2  
}

//SPI2数据发+收程序(主要用于发送)
u8 SPI2_SendByte(u8 Byte){ //通过SPI2口发送1个数据,同时接收1个数据
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET); //如果发送寄存器数据没有发送完,循环等待
	SPI_I2S_SendData(SPI2,Byte);  //往发送寄存器写入要发送的数据
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //如果接受寄存器没有收到数据,循环
	return SPI_I2S_ReceiveData(SPI2);
}

ch376.h

#ifndef __CH376_H
#define __CH376_H	 
#include "sys.h"
#include "spi.h"
#include "delay.h"
#include "ch376inc.h"

#define CH376_INTPORT		GPIOA	//定义IO接口
#define CH376_INT			GPIO_Pin_15	//定义IO接口


void 	CH376_PORT_INIT( void ); /* CH376通讯接口初始化 */
void	xEndCH376Cmd( void ); /* 结束SPI命令 */
void 	xWriteCH376Cmd( u8 mCmd ); /* 向CH376写命令 */
void 	xWriteCH376Data( u8 mData );/* 向CH376写数据 */
u8 	xReadCH376Data( void );	/* 从CH376读数据 */
u8 	Query376Interrupt( void );/* 查询CH376中断(INT#引脚为低电平) */
u8 	mInitCH376Host( void ); /* 初始化CH376 */

		 				    
#endif

ch376.c

#include "CH376.h"


/*******************************************************************************
* 函  数  名      : CH376_PORT_INIT
* 描      述      : 由于使用软件模拟SPI读写时序,所以进行初始化.
*                   如果是硬件SPI接口,那么可使用mode3(CPOL=1&CPHA=1)或
*                   mode0(CPOL=0&CPHA=0),CH376在时钟上升沿采样输入,下降沿输出,数
*                   据位是高位在前.
*******************************************************************************/
void CH376_PORT_INIT(void){ //CH376的SPI接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //选择端口号                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(GPIOA,&GPIO_InitStructure);	

	GPIO_SetBits(CH376_INTPORT,CH376_INT); //中断输入脚拉高电平
	GPIO_SetBits(SPI2PORT,SPI2_NSS); //片选接口接高电平
}
/*******************************************************************************
* 函  数  名      : xEndCH376Cmd   结束命令.
*******************************************************************************/ 
void xEndCH376Cmd(void){ //结束命令
	GPIO_SetBits(SPI2PORT,SPI2_NSS); //SPI片选无效,结束CH376命令
}
/*******************************************************************************
SPI输出8个位数据.    * 发送: u8 d:要发送的数据.
*******************************************************************************/
void Spi376OutByte(u8 d){ //SPI发送一个字节数据 
   SPI2_SendByte(d);	 
}
/*******************************************************************************
* 描      述      : SPI接收8个位数据.  u8 d:接收到的数据.
*******************************************************************************/
u8 Spi376InByte(void){	//SPI接收一个字节数据
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //如果接受寄存器没有收到数据,循环
	return SPI_I2S_ReceiveData(SPI2);
}
/*******************************************************************************
* 描      述      : 向CH376写  命令.
* 输      入      : u8 mCmd:要发送的命令.
*******************************************************************************/
void xWriteCH376Cmd(u8 mCmd){
	GPIO_SetBits(SPI2PORT,SPI2_NSS);   /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */
	delay_us(20);
/* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出
*  方向,SPI_SDO为输入方向 */
	GPIO_ResetBits(SPI2PORT,SPI2_NSS);     /* SPI片选有效 */
	Spi376OutByte( mCmd );  /* 发出命令码 */
	delay_us(1700);   /* 延时1.5mS确保读写周期大于1.5mS,或者用上面一行的状态查询代替 */
}
/*******************************************************************************
* 描      述      : 向CH376写   数据.
* 输      入      : u8 mData:
*                   要发送的数据.
*******************************************************************************/
void xWriteCH376Data(u8 mData){
	Spi376OutByte( mData );
	delay_us(800);  /* 确保读写周期大于0.6mS */
}
/*******************************************************************************
* 函  数  名      : xReadCH376Data
* 描      述      : 从CH376读数据.
*******************************************************************************/
u8 xReadCH376Data(void){
	u8 i;
	delay_us(10);
	i = SPI2_SendByte(0xFF);
	return(i);
}
/*******************************************************************************
* 描      述      : 查询CH376中断(INT#低电平).
* 返      回      : 0:无中断.       1:有中断.
*******************************************************************************/
u8 Query376Interrupt(void){
	u8 i;
	i = GPIO_ReadInputDataBit(CH376_INTPORT,CH376_INT); 	
	return( i ); 
}			
/*******************************************************************************
* 描      述      : 初始化CH376.
* 返      回      : FALSE:无中断.  TRUE:有中断.
*******************************************************************************/
u8 mInitCH376Host(void){
	u8	res;	
	delay_ms(600);
	CH376_PORT_INIT( );           /* 接口硬件初始化 */
	xWriteCH376Cmd( CMD11_CHECK_EXIST );    /* 测试单片机与CH376之间的通讯接口 */
	xWriteCH376Data( 0x55 );
	res = xReadCH376Data( );
//	printf("res =%02x \n",(unsigned short)res);
	xEndCH376Cmd( );
	if ( res != 0xAA ) return( ERR_USB_UNKNOWN );  /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */
	xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */
	xWriteCH376Data( 0x06 ); //06H=已启用的主机方式并且自动产生SOF包  ???
	delay_us(20);
	res = xReadCH376Data( );
//	printf("res =%02x \n",(unsigned short)res);
	xEndCH376Cmd( );

	if ( res == CMD_RET_SUCCESS ){  //RES=51  命令操作成功
	    return( USB_INT_SUCCESS ); //USB事务或者传输操作成功 
	}else{
	    return( ERR_USB_UNKNOWN );/* 设置模式错误 */
	}
}

ch376inc.h

官方提供

filesys.h


#ifndef	__FILESYS_H__
#define __FILESYS_H__
#include "sys.h"
#include "ch376.h"
#include"CH376INC.H"

#define	STRUCT_OFFSET( s, m )	( (UINT8)( & ((s *)0) -> m ) )							/* 定义获取结构成员相对偏移地址的宏 */
#ifdef	EN_LONG_NAME
#ifndef	LONG_NAME_BUF_LEN
#define	LONG_NAME_BUF_LEN	( LONG_NAME_PER_DIR * 20 )									/* 自行定义的长文件名缓冲区长度,最小值为LONG_NAME_PER_DIR*1 */
#endif
#endif

UINT8	CH376ReadBlock( PUINT8 buf );  													/* 从当前主机端点的接收缓冲区读取数据块,返回长度 */

UINT8	CH376WriteReqBlock( PUINT8 buf );  												/* 向内部指定缓冲区写入请求的数据块,返回长度 */

void	CH376WriteHostBlock( PUINT8 buf, UINT8 len );  									/* 向USB主机端点的发送缓冲区写入数据块 */

void	CH376WriteOfsBlock( PUINT8 buf, UINT8 ofs, UINT8 len );  						/* 向内部缓冲区指定偏移地址写入数据块 */

void	CH376SetFileName( PUINT8 name );  												/* 设置将要操作的文件的文件名 */

UINT32	CH376Read32bitDat( void );  													/* 从CH376芯片读取32位的数据并结束命令 */

UINT8	CH376ReadVar8( UINT8 var );  													/* 读CH376芯片内部的8位变量 */

void	CH376WriteVar8( UINT8 var, UINT8 dat );  										/* 写CH376芯片内部的8位变量 */

UINT32	CH376ReadVar32( UINT8 var );  													/* 读CH376芯片内部的32位变量 */

void	CH376WriteVar32( UINT8 var, UINT32 dat );  										/* 写CH376芯片内部的32位变量 */

void	CH376EndDirInfo( void );  														/* 在调用CH376DirInfoRead获取FAT_DIR_INFO结构之后应该通知CH376结束 */

UINT32	CH376GetFileSize( void );  														/* 读取当前文件长度 */

UINT8	CH376GetDiskStatus( void );                                                     /* 获取磁盘和文件系统的工作状态 */

UINT8	CH376GetIntStatus( void );                                                      /* 获取中断状态并取消中断请求 */

#ifndef	NO_DEFAULT_CH376_INT
UINT8	Wait376Interrupt( void );                                                       /* 等待CH376中断(INT#低电平),返回中断状态码, 超时则返回ERR_USB_UNKNOWN */
#endif

UINT8	CH376SendCmdWaitInt( UINT8 mCmd );                                              /* 发出命令码后,等待中断 */

UINT8	CH376SendCmdDatWaitInt( UINT8 mCmd, UINT8 mDat );                               /* 发出命令码和一字节数据后,等待中断 */

UINT8	CH376DiskReqSense( void );                                                      /* 检查USB存储器错误 */

UINT8	CH376DiskConnect( void );                                                       /* 检查U盘是否连接,不支持SD卡 */

UINT8	CH376DiskMount( void );                                                         /* 初始化磁盘并测试磁盘是否就绪 */

UINT8	CH376FileOpen( PUINT8 name );                                                   /* 在根目录或者当前目录下打开文件或者目录(文件夹) */

UINT8	CH376FileCreate( PUINT8 name );                                                 /* 在根目录或者当前目录下新建文件,如果文件已经存在那么先删除 */

UINT8	CH376DirCreate( PUINT8 name );                                                  /* 在根目录下新建目录(文件夹)并打开,如果目录已经存在那么直接打开 */

UINT8	CH376SeparatePath( PUINT8 path );                                               /* 从路径中分离出最后一级文件名或者目录(文件夹)名,返回最后一级文件名或者目录名的字节偏移 */

UINT8	CH376FileOpenDir( PUINT8 PathName, UINT8 StopName );                            /* 打开多级目录下的文件或者目录的上级目录,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */
/* StopName 指向最后一级文件名或者目录名 */

UINT8	CH376FileOpenPath( PUINT8 PathName );                                           /* 打开多级目录下的文件或者目录(文件夹),支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

UINT8	CH376FileCreatePath( PUINT8 PathName );                                         /* 新建多级目录下的文件,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

#ifdef	EN_DIR_CREATE
UINT8	CH376DirCreatePath( PUINT8 PathName );                                          /* 新建多级目录下的目录(文件夹)并打开,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */
#endif

UINT8	CH376FileErase( PUINT8 PathName );                                              /* 删除文件,如果已经打开则直接删除,否则对于文件会先打开再删除,支持多级目录路径 */

UINT8	CH376FileClose( UINT8 UpdateSz );                                               /* 关闭当前已经打开的文件或者目录(文件夹) */

UINT8	CH376DirInfoRead( void );                                                       /* 读取当前文件的目录信息 */

UINT8	CH376DirInfoSave( void );                                                       /* 保存文件的目录信息 */

UINT8	CH376ByteLocate( UINT32 offset );                                               /* 以字节为单位移动当前文件指针 */

UINT8	CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount );                /* 以字节为单位从当前位置读取数据块 */

UINT8	CH376ByteWrite( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount );               /* 以字节为单位向当前位置写入数据块 */

UINT8	CH376DiskCapacity( PUINT32 DiskCap );                                           /* 查询磁盘物理容量,扇区数 */

UINT8   CH376DiskQuery( PUINT32 DiskFre );                                              /* 查询磁盘剩余空间信息,扇区数 */

UINT8	CH376SecLocate( UINT32 offset );                                                /* 以扇区为单位移动当前文件指针 */

#ifdef	EN_SECTOR_ACCESS

UINT8	CH376DiskReadSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount );           /* 从U盘读取多个扇区的数据块到缓冲区,不支持SD卡 */

UINT8	CH376DiskWriteSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount );          /* 将缓冲区中的多个扇区的数据块写入U盘,不支持SD卡 */

UINT8	CH376SecRead( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount );                   /* 以扇区为单位从当前位置读取数据块,不支持SD卡 */

UINT8	CH376SecWrite( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount );                  /* 以扇区为单位在当前位置写入数据块,不支持SD卡 */

#endif

#ifdef	EN_LONG_NAME

UINT8	CH376LongNameWrite( PUINT8 buf, UINT16 ReqCount );                              /* 长文件名专用的字节写子程序 */

UINT8	CH376CheckNameSum( PUINT8 DirName );                                            /* 计算长文件名的短文件名检验和,输入为无小数点分隔符的固定11字节格式 */

UINT8	CH376LocateInUpDir( PUINT8 PathName );                                          /* 在上级目录(文件夹)中移动文件指针到当前文件目录信息所在的扇区 */
/* 另外,顺便将当前文件目录信息所在的扇区的前一个扇区的LBA地址写入CH376内部VAR_FAT_DIR_LBA变量(为了方便收集长文件名时向前搜索,否则要多移动一次) */
/* 使用了全局缓冲区GlobalBuf的前12个字节 */

UINT8	CH376GetLongName( PUINT8 PathName, PUINT8 LongName );                           /* 由短文件名或者目录(文件夹)名获得相应的长文件名 */
/* 需要输入短文件名的完整路径PathName,需要提供缓冲区接收长文件名LongName(以UNICODE小端编码,以双0结束) */
/* 使用了全局缓冲区GlobalBuf的前34个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)+2 */

UINT8	CH376CreateLongName( PUINT8 PathName, PUINT8 LongName );                        /* 新建具有长文件名的文件,关闭文件后返回,LongName输入路径必须在RAM中 */
/* 需要输入短文件名的完整路径PathName(请事先参考FAT规范由长文件名自行产生),需要输入以UNICODE小端编码的以双0结束的长文件名LongName */
/* 使用了全局缓冲区GlobalBuf的前64个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)*2 */

#endif

#endif


filesys.c

官方提供

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "spi.h"
#include "ch376.h"
#include"filesys.h"

int main (void){//主程序
	u8 s;
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"  U DISK TEST   "); //显示字符串
	//CH376初始化	
	SPI2_Init();//SPI接口初始化
	if(mInitCH376Host()== USB_INT_SUCCESS){//CH376初始化
		OLED_DISPLAY_8x16_BUFFER(4,"   CH376 OK!    "); //显示字符串
	}
	while(1){
		s = CH376DiskConnect();	 //读出U盘的状态 
		if(s == USB_INT_SUCCESS){ //检查U盘是否连接//等待U盘插入
			OLED_DISPLAY_8x16_BUFFER(6," U DISK Ready!  "); //显示字符串
		}else{
			OLED_DISPLAY_8x16_BUFFER(6,"                "); //显示字符串		
		} 
		delay_ms(500); //刷新的间隔
	}
}

3.U盘读写文件程序

filesys.h


#ifndef	__FILESYS_H__
#define __FILESYS_H__
#include "sys.h"
#include "ch376.h"
#include"CH376INC.H"

#define	STRUCT_OFFSET( s, m )	( (UINT8)( & ((s *)0) -> m ) )							/* 定义获取结构成员相对偏移地址的宏 */
#ifdef	EN_LONG_NAME
#ifndef	LONG_NAME_BUF_LEN
#define	LONG_NAME_BUF_LEN	( LONG_NAME_PER_DIR * 20 )									/* 自行定义的长文件名缓冲区长度,最小值为LONG_NAME_PER_DIR*1 */
#endif
#endif

UINT8	CH376ReadBlock( PUINT8 buf );  													/* 从当前主机端点的接收缓冲区读取数据块,返回长度 */

UINT8	CH376WriteReqBlock( PUINT8 buf );  												/* 向内部指定缓冲区写入请求的数据块,返回长度 */

void	CH376WriteHostBlock( PUINT8 buf, UINT8 len );  									/* 向USB主机端点的发送缓冲区写入数据块 */

void	CH376WriteOfsBlock( PUINT8 buf, UINT8 ofs, UINT8 len );  						/* 向内部缓冲区指定偏移地址写入数据块 */

void	CH376SetFileName( PUINT8 name );  												/* 设置将要操作的文件的文件名 */

UINT32	CH376Read32bitDat( void );  													/* 从CH376芯片读取32位的数据并结束命令 */

UINT8	CH376ReadVar8( UINT8 var );  													/* 读CH376芯片内部的8位变量 */

void	CH376WriteVar8( UINT8 var, UINT8 dat );  										/* 写CH376芯片内部的8位变量 */

UINT32	CH376ReadVar32( UINT8 var );  													/* 读CH376芯片内部的32位变量 */

void	CH376WriteVar32( UINT8 var, UINT32 dat );  										/* 写CH376芯片内部的32位变量 */

void	CH376EndDirInfo( void );  														/* 在调用CH376DirInfoRead获取FAT_DIR_INFO结构之后应该通知CH376结束 */

UINT32	CH376GetFileSize( void );  														/* 读取当前文件长度 */

UINT8	CH376GetDiskStatus( void );                                                     /* 获取磁盘和文件系统的工作状态 */

UINT8	CH376GetIntStatus( void );                                                      /* 获取中断状态并取消中断请求 */

#ifndef	NO_DEFAULT_CH376_INT
UINT8	Wait376Interrupt( void );                                                       /* 等待CH376中断(INT#低电平),返回中断状态码, 超时则返回ERR_USB_UNKNOWN */
#endif

UINT8	CH376SendCmdWaitInt( UINT8 mCmd );                                              /* 发出命令码后,等待中断 */

UINT8	CH376SendCmdDatWaitInt( UINT8 mCmd, UINT8 mDat );                               /* 发出命令码和一字节数据后,等待中断 */

UINT8	CH376DiskReqSense( void );                                                      /* 检查USB存储器错误 */

UINT8	CH376DiskConnect( void );                                                       /* 检查U盘是否连接,不支持SD卡 */

UINT8	CH376DiskMount( void );                                                         /* 初始化磁盘并测试磁盘是否就绪 */

UINT8	CH376FileOpen( PUINT8 name );                                                   /* 在根目录或者当前目录下打开文件或者目录(文件夹) */

UINT8	CH376FileCreate( PUINT8 name );                                                 /* 在根目录或者当前目录下新建文件,如果文件已经存在那么先删除 */

UINT8	CH376DirCreate( PUINT8 name );                                                  /* 在根目录下新建目录(文件夹)并打开,如果目录已经存在那么直接打开 */

UINT8	CH376SeparatePath( PUINT8 path );                                               /* 从路径中分离出最后一级文件名或者目录(文件夹)名,返回最后一级文件名或者目录名的字节偏移 */

UINT8	CH376FileOpenDir( PUINT8 PathName, UINT8 StopName );                            /* 打开多级目录下的文件或者目录的上级目录,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */
/* StopName 指向最后一级文件名或者目录名 */

UINT8	CH376FileOpenPath( PUINT8 PathName );                                           /* 打开多级目录下的文件或者目录(文件夹),支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

UINT8	CH376FileCreatePath( PUINT8 PathName );                                         /* 新建多级目录下的文件,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */

#ifdef	EN_DIR_CREATE
UINT8	CH376DirCreatePath( PUINT8 PathName );                                          /* 新建多级目录下的目录(文件夹)并打开,支持多级目录路径,支持路径分隔符,路径长度不超过255个字符 */
#endif

UINT8	CH376FileErase( PUINT8 PathName );                                              /* 删除文件,如果已经打开则直接删除,否则对于文件会先打开再删除,支持多级目录路径 */

UINT8	CH376FileClose( UINT8 UpdateSz );                                               /* 关闭当前已经打开的文件或者目录(文件夹) */

UINT8	CH376DirInfoRead( void );                                                       /* 读取当前文件的目录信息 */

UINT8	CH376DirInfoSave( void );                                                       /* 保存文件的目录信息 */

UINT8	CH376ByteLocate( UINT32 offset );                                               /* 以字节为单位移动当前文件指针 */

UINT8	CH376ByteRead( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount );                /* 以字节为单位从当前位置读取数据块 */

UINT8	CH376ByteWrite( PUINT8 buf, UINT16 ReqCount, PUINT16 RealCount );               /* 以字节为单位向当前位置写入数据块 */

UINT8	CH376DiskCapacity( PUINT32 DiskCap );                                           /* 查询磁盘物理容量,扇区数 */

UINT8   CH376DiskQuery( PUINT32 DiskFre );                                              /* 查询磁盘剩余空间信息,扇区数 */

UINT8	CH376SecLocate( UINT32 offset );                                                /* 以扇区为单位移动当前文件指针 */

#ifdef	EN_SECTOR_ACCESS

UINT8	CH376DiskReadSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount );           /* 从U盘读取多个扇区的数据块到缓冲区,不支持SD卡 */

UINT8	CH376DiskWriteSec( PUINT8 buf, UINT32 iLbaStart, UINT8 iSectorCount );          /* 将缓冲区中的多个扇区的数据块写入U盘,不支持SD卡 */

UINT8	CH376SecRead( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount );                   /* 以扇区为单位从当前位置读取数据块,不支持SD卡 */

UINT8	CH376SecWrite( PUINT8 buf, UINT8 ReqCount, PUINT8 RealCount );                  /* 以扇区为单位在当前位置写入数据块,不支持SD卡 */

#endif

#ifdef	EN_LONG_NAME

UINT8	CH376LongNameWrite( PUINT8 buf, UINT16 ReqCount );                              /* 长文件名专用的字节写子程序 */

UINT8	CH376CheckNameSum( PUINT8 DirName );                                            /* 计算长文件名的短文件名检验和,输入为无小数点分隔符的固定11字节格式 */

UINT8	CH376LocateInUpDir( PUINT8 PathName );                                          /* 在上级目录(文件夹)中移动文件指针到当前文件目录信息所在的扇区 */
/* 另外,顺便将当前文件目录信息所在的扇区的前一个扇区的LBA地址写入CH376内部VAR_FAT_DIR_LBA变量(为了方便收集长文件名时向前搜索,否则要多移动一次) */
/* 使用了全局缓冲区GlobalBuf的前12个字节 */

UINT8	CH376GetLongName( PUINT8 PathName, PUINT8 LongName );                           /* 由短文件名或者目录(文件夹)名获得相应的长文件名 */
/* 需要输入短文件名的完整路径PathName,需要提供缓冲区接收长文件名LongName(以UNICODE小端编码,以双0结束) */
/* 使用了全局缓冲区GlobalBuf的前34个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)+2 */

UINT8	CH376CreateLongName( PUINT8 PathName, PUINT8 LongName );                        /* 新建具有长文件名的文件,关闭文件后返回,LongName输入路径必须在RAM中 */
/* 需要输入短文件名的完整路径PathName(请事先参考FAT规范由长文件名自行产生),需要输入以UNICODE小端编码的以双0结束的长文件名LongName */
/* 使用了全局缓冲区GlobalBuf的前64个字节,sizeof(GlobalBuf)>=sizeof(FAT_DIR_INFO)*2 */

#endif

#endif


filesys.c

自带

main.c

#include <string.h>
#include <stdio.h>
#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "spi.h"
#include "ch376.h"
#include"filesys.h"

u8 buf[128]; 

int main (void){//主程序
	u8 s,i;
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"  U DISK TEST   "); //显示字符串
	//CH376初始化	
	SPI2_Init();//SPI接口初始化
	if(mInitCH376Host()== USB_INT_SUCCESS){//CH376初始化
		OLED_DISPLAY_8x16_BUFFER(4,"   CH376 OK!    "); //显示字符串
	}
	while(1){
		while ( CH376DiskConnect( ) != USB_INT_SUCCESS ) delay_ms(100);  // 检查U盘是否连接,等待U盘拔出
		OLED_DISPLAY_8x16_BUFFER(6," U DISK Ready!  "); //显示字符串
		delay_ms(200); //每次操作后必要的延时
		for ( i = 0; i < 100; i ++ ){ 
			delay_ms( 50 );
			s = CH376DiskMount( );  //初始化磁盘并测试磁盘是否就绪.   
			if ( s == USB_INT_SUCCESS ) /* 准备好 */
			break;                                          
			else if ( s == ERR_DISK_DISCON )/* 检测到断开,重新检测并计时 */
			break;  
			if ( CH376GetDiskStatus( ) >= DEF_DISK_MOUNTED && i >= 5 ) /* 有的U盘总是返回未准备好,不过可以忽略,只要其建立连接MOUNTED且尝试5*50mS */
			break; 
		}
		OLED_DISPLAY_8x16_BUFFER(6," U DISK INIT!   "); //显示字符串
		delay_ms(200); //每次操作后必要的延时
      	s=CH376FileCreatePath( "/洋桃.TXT" ); // 新建多级目录下的文件,支持多级目录路径,输入缓冲区必须在RAM中 
		delay_ms(200); //每次操作后必要的延时
		s = sprintf( (char *)buf , "洋桃电子 www.DoYoung.net/YT");
		s=CH376ByteWrite( buf,s, NULL ); // 以字节为单位向当前位置写入数据块 
		delay_ms(200); //每次操作后必要的延时
		s=CH376FileClose( TRUE );   // 关闭文件,对于字节读写建议自动更新文件长度 
		OLED_DISPLAY_8x16_BUFFER(6," U DISK SUCCESS "); //显示字符串
		while ( CH376DiskConnect( ) == USB_INT_SUCCESS ) delay_ms(500);  // 检查U盘是否连接,等待U盘拔出
		OLED_DISPLAY_8x16_BUFFER(6,"                "); //显示字符串
		delay_ms(200); //每次操作后必要的延时
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/838918.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

丁香园:2023药品带量采购政策及趋势分析报告(附下载)

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 化药&#xff1a;经多次集采&#xff0c;其规则与模式已比较成熟&#xff0c;是药品集采主要品种 中成药&#xff1a;由湖北牵头开展2 个批次&#xff0c;覆盖全国 31 省共 33产品组&#xff0c;此外…

谈谈对Android音视频开发的探究

在日常生活中&#xff0c;视频类应用占据了我们越来越多的时间&#xff0c;各大公司也纷纷杀入这个战场&#xff0c;不管是抖音、快手等短视频类型&#xff0c;虎牙、斗鱼等直播类型&#xff0c;腾讯视频、爱奇艺、优酷等长视频类型&#xff0c;还是Vue、美拍等视频编辑美颜类型…

【RL】Wasserstein距离-GAN背后的直觉

一、说明 在本文中&#xff0c;我们将阅读有关Wasserstein GANs的信息。具体来说&#xff0c;我们将关注以下内容&#xff1a;i&#xff09;什么是瓦瑟斯坦距离&#xff1f;&#xff0c;ii&#xff09;为什么要使用它&#xff1f;iii&#xff09; 我们如何使用它来训练 GAN&…

软件工程专业应该学什么?

昨天&#xff0c;我朋友的孩子报考了软件工程专业&#xff0c;问我软件工程到底学啥&#xff1f;所以我给他开列了一个书单。 现在高校开了一堆花名头的专业&#xff1a; 偏技术类&#xff1a;云计算、大数据、人工智能、物联网 偏应用类&#xff1a;电子商务、信息管理 但我个…

flink1.17 eventWindow不要配置processTrigger

理论上可以eventtime processtime混用,但是下面代码测试发现bug,输入一条数据会一直输出. flink github无法提bug/问题. apache jira账户新建后竟然flink又需要一个账户,放弃 bug复现操作 idea运行代码后 往source kafka发送一条数据 a,1,1690304400000 可以看到无限输出…

.net 6 efcore一个model映射到多张表(非使用IEntityTypeConfiguration)

现在有两张表&#xff0c;结构一模一样&#xff0c;我又不想创建两个一模一样的model&#xff0c;就想一个model映射到两张表 废话不多说直接上代码 安装依赖包 创建model namespace oneModelMultiTable.Model {public class Test{public int id { get; set; }public string…

【C语言进阶】数据的存储----浮点型篇

&#x1f341; 博客主页:江池俊的博客 &#x1f4ab;收录专栏&#xff1a;C语言—探索高效编程的基石 &#x1f4bb; 其他专栏&#xff1a;数据结构探索 ​&#x1f4a1;代码仓库&#xff1a;江池俊的代码仓库 &#x1f3aa; 社区&#xff1a;GeekHub &#x1f341; 如果觉得博…

部分常用CSS样式

目录 1.字体样式 2.文本样式 3.鼠标样式 cursor 4.背景样式 5.列表样式 6.CSS伪类 7.盒子模型 1.字体样式 font-family 字体类型&#xff1a;隶书” “楷体” font-size 字体大小&#xff1a;像素px font-weight 字体粗细&#xff1a;bold 定义粗体字…

8月5日上课内容 nginx的优化和防盗链

全部都是面试题 nginx的优化和防盗链 重点就是优化&#xff1a; 每一个点都是面试题&#xff0c;非常重要&#xff0c;都是面试题 1、隐藏版本号&#xff08;重点&#xff0c;一定要会&#xff09; 备份 cp nginx.conf nginx.conf.bak.2023.0805 方法一&#xff1a;修改配…

拦截器在SpringBoot中使用,HandlerInterceptor,WebMvcConfigurer

拦截器在Controller之前执行。 用于权限校验&#xff0c;日志记录&#xff0c;性能监控 在SpringBoot中使用 创建拦截器类&#xff1a;首先&#xff0c;创建一个Java类来实现拦截器逻辑。拦截器类应该实现Spring提供的HandlerInterceptor接口。实现拦截器方法&#xff1a;拦…

探索PostgreSQL的新功能:最新版本更新解析

PostgreSQL作为一种强大而开源的关系型数据库管理系统&#xff0c;不断在不断进化和改进。每一次的版本更新都带来了更多功能和改进&#xff0c;让用户在处理大规模数据和复杂查询时体验更好的性能和功能。在本文中&#xff0c;我们将深入探索PostgreSQL的最新版本更新&#xf…

进程上下文切换以及应用场景

各个进程之间是共享 CPU 资源的&#xff0c;在不同的时候进程之间需要切换&#xff0c;让不同的进程可以在 CPU 执行&#xff0c;那么这个一个进程切换到另一个进程运行&#xff0c;称为进程的上下文切换。 在详细说进程上下文切换前&#xff0c;我们先来看看 CPU 上下文切换 大…

VX-API-Gateway开源网关技术的使用记录

VX-API-Gateway开源网关技术的使用记录 官网地址 https://mirren.gitee.io/vx-api-gateway-doc/ VX-API-Gateway(以下称为VX-API)是基于Vert.x (java)开发的 API网关, 是一个分布式、全异步、高性能、可扩展、轻量级的可视化配置的API网关服务官网下载程序zip包 访问 https:/…

深入浅出 Typescript

TypeScript 是 JavaScript 的一个超集&#xff0c;支持 ECMAScript 6 标准&#xff08;ES6 教程&#xff09;。 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大型应用&#xff0c;它可以编译成纯 JavaScript&#xff0c;编译出来的 JavaScript …

AtcoderABC226场

A - Round decimalsA - Round decimals 题目大意 给定一个实数X&#xff0c;它最多可以使用三位小数表示&#xff0c;而且X的小数点后有三位小数。将X四舍五入到最接近的整数并打印结果。 思路分析 可以使用round函数进行四舍五入 知识点 round(x) 是一个用来对数字进行四…

SpringIoc-个人学习笔记

Spring的Ioc、DI、AOP思想 Ioc Ioc思想&#xff1a;Inversion of Control&#xff0c;控制反转&#xff0c;在创建Bean的权利反转给第三方 DI DI思想&#xff1a;Dependency Injection&#xff0c;依赖注入&#xff0c;强调Bean之间的关系&#xff0c;这种关系由第三方负责去设…

Redis 报错 RedisConnectionException: Unable to connect to x.x.x.x:6379

文章目录 Redis报错类型可能解决方案 Redis报错类型 org.springframework.data.redis.connection. spingboot调用redis出错 PoolException: Could not get a resource from the pool; 连接池异常:无法从池中获取资源; nested exception is io.lettuce.core. 嵌套异常 RedisConn…

针对高可靠性和高性能优化的1200V碳化硅沟道MOSFET

目录 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance摘要信息解释研究了什么文章创新点文章的研究方法文章的结论 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance 摘要 本文详…

FPGA----UltraScale+系列的PS侧与PL侧通过AXI-HP交互(全网唯一最详)附带AXI4协议校验IP使用方法

1、之前写过一篇关于ZYNQ系列通用的PS侧与PL侧通过AXI-HP通道的文档&#xff0c;下面是链接。 FPGA----ZCU106基于axi-hp通道的pl与ps数据交互&#xff08;全网唯一最详&#xff09;_zcu106调试_发光的沙子的博客-CSDN博客大家好&#xff0c;今天给大家带来的内容是&#xff0…

获取k8s scale资源对象的命令

kubectl get --raw /apis/<apiGroup>/<apiVersion>/namespaces/<namespaceName>/<resourceKind>/<resourceName>/scale 说明&#xff1a;scale资源对象用来水平扩展k8s资源对象的副本数&#xff0c;它是作为一种k8s资源对象的子资源存在&#xf…