文章目录:
一:触摸按键
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){ //Aif(!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}} //按键判断在此结束 //Bif(!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}} //按键判断在此结束 //Cif(!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}} //按键判断在此结束 //Dif(!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~99if(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)); //清除EV5I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Transmitter);//发送器件地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除EV6I2C_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)); //清除 EV5I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //写入器件地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6I2C_Cmd(I2C1,ENABLE);I2C_SendData(I2C1,readAddr); //发送读的地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //清除 EV8I2C_GenerateSTART(I2C1,ENABLE); //开启信号while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); //将器件地址传出,主机为读while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //清除EV6while(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);//调用库函数将数据取出到 pBufferpBuffer++; //指针移位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" //引入字体 ASCIIvoid 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++){ //设置起始页地址为0xB0I2C_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表对应要减512c++;}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[] = { // ASCII0x00,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/汉字选择——>ASCLL4.字库选择——>Asc8X16E5.点击魔法按钮:参数确认——>点击ASC按钮汉字数据表生成 设置参数:1.输出格式——>结构型格式:从左到右从上到下2.取模方式——>纵向8点下高位3.ASC/汉字选择——>汉字4.字库选择——>宋体16点阵5.点击魔法按钮:参数确认——>点击ASC按钮6.在下方输入我们想要的汉字——>点击A输入字串图片数据表生成 设置参数:1.输出格式——>结构型格式:从左到右从上到下2.取模方式——>纵向8点下高位3.输出大小——>X128 Y644.点击魔法按钮:参数确认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++){ //设置起始页地址为0xB0I2C_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表对应要减512c++;}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-DPGPIO_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); //各接口置0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_D); //各接口置1delay_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);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C); //1delay_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 DGPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A| STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1delay_ms(speed); //延时STEP_MOTOR_OFF(); //进入断电状态,防电机过热 }void STEP_MOTOR_4L (u8 speed){//电机逆时针,4拍,速度快,力小 D C B AGPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1delay_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);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1delay_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);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1delay_ms(speed); //延时GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1delay_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);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A);//1break;case 1:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//1break;case 2:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_C | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B);//1break;case 3:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//1break;case 4:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_D);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C);//1break;case 5:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_C | STEP_MOTOR_D);//1break;case 6:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_D);//1break;case 7:GPIO_ResetBits(STEP_MOTOR_PORT,STEP_MOTOR_B | STEP_MOTOR_C);//0GPIO_SetBits(STEP_MOTOR_PORT,STEP_MOTOR_A | STEP_MOTOR_D);//1break;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.hBasic文件夹——>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)串口3extern 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接口是PB11GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//接口模式 浮空输入GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//设置USART3的TX接口是PB10GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度50MHzGPIO_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);//配置串口3USART_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)串口3extern 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_ENABLENVIC_InitTypeDef NVIC_InitStructure; #endifRCC_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); //初始化IOGPIO_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_4tqCAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~ CAN_BS1_16tqCAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tqCAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1 CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1 //设置过滤器CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位宽 CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位IDCAN_FilterInitStructure.CAN_FilterIdLow=0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASKCAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器0CAN_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; // 主优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure); #endifreturn 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通道1DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_AddressDMA_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通道1DMA_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);//使能ADC1ADC_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通道1DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_AddressDMA_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通道1DMA_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);//使能ADC1ADC_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通道1DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //定义 DMA通道外设基地址=ADC1_DR_AddressDMA_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通道1DMA_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);//使能ADC1ADC_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)串口3extern 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.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //USART1_RX PA.10GPIO_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 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_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清0if(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++; //数据长度计数加1if(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接口是PA3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //设置USART2的TX接口是PA2GPIO_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 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //优先级3NVIC_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接口是PB11GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//接口模式 浮空输入GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//设置USART3的TX接口是PB10GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度50MHzGPIO_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 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级3NVIC_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);//配置串口3USART_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){ //音量加1USART3_printf("\x7e\x03\x15\x16\xef"); } void MY1690_VDOWN(void){ //音量减1USART3_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 表示收到STOPMP3=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();函数中自带判断是不是首次使用RTC2,使用 RTC_Get();读出时间。读出的数据存放在:年 ryear (16位)月 rmon (以下都是8位)日 rday时 rhour分 rmin秒 rsec周 rweek3,使用 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模块不需要重新配置,且掉电后依靠后备电池依然运行//但是每次上电后,还是要使能RTCCLKRCC_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);//配置完成后,向后备寄存器中写特殊字符0xA5A5RTC_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 表示收到STOPOLED_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.hBasic文件夹——>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 #endifUINT8 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 */ #endifUINT8 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个字符 */ #endifUINT8 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_ACCESSUINT8 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_NAMEUINT8 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 #endifUINT8 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 */ #endifUINT8 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个字符 */ #endifUINT8 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_ACCESSUINT8 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_NAMEUINT8 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); //每次操作后必要的延时} }