• 方案介绍
    • 前言
    • 一 材料准备
    • 二 引脚使用
    • 三 代码编写
    • 四 程序说明
    • 五 参考
  • 附件下载
  • 相关推荐
申请入驻 产业图谱

【做着玩系列-开源】 STM32版电子沙漏(定时模式+循环模式)

02/05 09:03
1547
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

联系方式.docx

共1个文件

前言

最近看到一款电子沙漏,感觉还挺好玩的,然后正好要准备过年了,闲工夫比较多,那咱也来凑凑热闹,不过看了开源广场,有个STM32版本的,但是不是STM32F103版本的,所以我在他的查考了他的代码后自己改写。

关注微信公众号--星之援工作室 发送关键字(电子沙漏)

代码含重要注释,开源,可自行移植

演示视频


stm32版本 电子沙漏

一 材料准备

1.开发板

STM32F103系列开发板 X 1 :做本次设备的主控芯片

2.MPU-6050模块

MPU6050 X 1:反馈方位

3.MAX7219点阵模块

MAX7219点阵模块 X 2:实现沙漏效果

4.按键模块

按键 X 3:实现按键功能

二 引脚使用

1.MPU-6050模块与按键模块

MPU-6050模块 按键模块
VCC 3.3 GND GND
GND GND IN1 PB12
SCL PB8 IN2 PB13
SDA PB9 IN3 PB14

 2.MAX7219与蜂鸣器

MAX7219-1 MAX7219-2
VCC 5 VCC 5
GND GND GND GND
DIN PA5 DIN PB5
CS PA4 CS PB4
CLK PA6 CLK PB6
蜂鸣器
OUT PB7 GND GND

三 代码编写

1.MPU6050部分

这边部分 我们主要用到了一个模拟的IIC通信,并且MPU6050的其他配置函数,都是由现成的,我们只要会读代码就好

 IIC.c

#include "IIC.h"
#include "delay.h"
#include "stdio.h"

/**************************实现函数********************************************
*函数原型:		void IIC_Init(void)
*功  能:		初始化I2C对应的接口引脚。
*******************************************************************************/
void IIC_Init(void)
{		
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(SCLK_GPIO_CLK | DATA_GPIO_CLK, ENABLE);

	GPIO_InitStructure.GPIO_Pin = SCLK_GPIO_PIN | DATA_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(SCLK_GPIO_PORT, &GPIO_InitStructure);

	IIC_SCL_1;
	IIC_SDA_1;	
}
// 配置双向I/O端口为输出态
static void SDA_OUT()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(DATA_GPIO_CLK, ENABLE);

	GPIO_InitStructure.GPIO_Pin = DATA_GPIO_PIN; // PC.10  DATA
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SCLK_GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.10
}
// 配置双向I/O端口为输入态
static void SDA_IN()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(DATA_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin = DATA_GPIO_PIN; // PC.10 DATA
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DATA_GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.10
}
/**************************实现函数********************************************
*函数原型:		void IIC_Start(void)
*功  能:		产生IIC起始信号
*******************************************************************************/
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出 
	IIC_SDA_1;	  	  
	IIC_SCL_1;
	delay_us(4);
 	IIC_SDA_0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL_0;//钳住I2C总线,准备发送或接收数据 
}

/**************************实现函数********************************************
*函数原型:		void IIC_Stop(void)
*功  能:	    //产生IIC停止信号
*******************************************************************************/	  
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL_0;
	IIC_SDA_0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL_1; 
	IIC_SDA_1;//发送I2C总线结束信号
	delay_us(4);							   	
}

/**************************实现函数********************************************
*函数原型:		uint8_t IIC_Wait_Ack(void)
*功  能:	    等待应答信号到来 
//返回值:1,接收应答失败
//        0,接收应答成功
*******************************************************************************/
uint8_t IIC_Wait_Ack(void)
{
	uint8_t ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA_1;delay_us(1);	   
	IIC_SCL_1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>50)
		{
			IIC_Stop();
			return 1;
		}
	  delay_us(1);
	}
	IIC_SCL_0;//时钟输出0 	   
	return 0;  
} 

/**************************实现函数********************************************
*函数原型:		void IIC_Ack(void)
*功  能:	    产生ACK应答
*******************************************************************************/
void IIC_Ack(void)
{
	IIC_SCL_0;
	SDA_OUT();
	IIC_SDA_0;
	delay_us(1);
	IIC_SCL_1;
	delay_us(1);
	IIC_SCL_0;
}
	
/**************************实现函数********************************************
*函数原型:		void IIC_NAck(void)
*功  能:	    产生NACK应答
*******************************************************************************/	    
void IIC_NAck(void)
{
	IIC_SCL_0;
	SDA_OUT();
	IIC_SDA_1;
	delay_us(1);
	IIC_SCL_1;
	delay_us(1);
	IIC_SCL_0;
}					 				     

/**************************实现函数********************************************
*函数原型:		void IIC_Send_Byte(uint8_t txd)
*功  能:	    IIC发送一个字节
*******************************************************************************/		  
void IIC_Send_Byte(uint8_t txd)
{                        
    uint8_t t;   
	SDA_OUT(); 	    
    IIC_SCL_0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        if( (txd&0x80)>>7 )
				{
					IIC_SDA_1;
				}
				else
				{
					IIC_SDA_0;
				}
					
				
    txd<<=1; 	  
		delay_us(1);   
		IIC_SCL_1;
		delay_us(1); 
		IIC_SCL_0;	
		delay_us(1);
    }	 
} 	 
   
/**************************实现函数********************************************
*函数原型:		uint8_t IIC_Read_Byte(unsigned char ack)
*功  能:	    //读1个字节,ack=1时,发送ACK,ack=0,发送nACK 
*******************************************************************************/  
uint8_t IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
	for(i=0;i<8;i++ )
	{
			IIC_SCL_0; 
			delay_us(1);
			IIC_SCL_1;
			receive<<=1;
			if(READ_SDA)receive++;   
	delay_us(1); 
	}					 
	if (ack){
		IIC_Ack(); //发送ACK  
	}
	else{
		IIC_NAck();//发送nACK  
	}
			
	return receive;
}

/**************************实现函数********************************************
*函数原型:		unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr)
*功  能:	    读取指定设备 指定寄存器的一个值
输入	I2C_Addr  目标设备地址
		addr	   寄存器地址
返回   读出来的值
*******************************************************************************/ 
unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr)
{
	unsigned char res=0;
	
	IIC_Start();	

	IIC_Send_Byte(I2C_Addr);	   //发送写命令
	res++;
	IIC_Wait_Ack();
	IIC_Send_Byte(addr); res++;  //发送地址
	IIC_Wait_Ack();	  
	//IIC_Stop();//产生一个停止条件	
	IIC_Start();
	IIC_Send_Byte(I2C_Addr+1); res++;          //进入接收模式			   
	IIC_Wait_Ack();
	res=IIC_Read_Byte(0);	   
    IIC_Stop();//产生一个停止条件

	return res;
}


/**************************实现函数********************************************
*函数原型:		uint8_t IICreadBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t *data)
*功  能:	    读取指定设备 指定寄存器的 length个值
输入	dev  目标设备地址
		reg	  寄存器地址
		length 要读的字节数
		*data  读出的数据将要存放的指针
返回   读出来的字节数量
*******************************************************************************/ 
uint8_t IICreadBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t *data){
  uint8_t count = 0;
	uint8_t temp;
	IIC_Start();
	IIC_Send_Byte(dev);	   //发送写命令
	IIC_Wait_Ack();
	IIC_Send_Byte(reg);   //发送地址
    IIC_Wait_Ack();	  
	IIC_Start();
	IIC_Send_Byte(dev+1);  //进入接收模式	
	IIC_Wait_Ack();
	
   for(count=0;count<length;count++){
		 
		 if(count!=(length-1)){
				temp = IIC_Read_Byte(1);  //带ACK的读数据
			}
		 	else{
				temp = IIC_Read_Byte(0);	 //最后一个字节NACK
			}  


		data[count] = temp;
	}
    IIC_Stop();//产生一个停止条件
    return count;
}

/**************************实现函数********************************************
*函数原型:		uint8_t IICwriteBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t* data)
*功  能:	    将多个字节写入指定设备 指定寄存器
输入	dev  目标设备地址
		reg	  寄存器地址
		length 要写的字节数
		*data  将要写的数据的首地址
返回   返回是否成功
*******************************************************************************/ 
uint8_t IICwriteBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t* data){
  
 	uint8_t count = 0;
	IIC_Start();
	IIC_Send_Byte(dev);	   //发送写命令
	IIC_Wait_Ack();
	IIC_Send_Byte(reg);   //发送地址
    IIC_Wait_Ack();	  
	for(count=0;count<length;count++){
		IIC_Send_Byte(data[count]); 
		IIC_Wait_Ack(); 
	 }
	IIC_Stop();//产生一个停止条件

    return 1; //status == 0;
}

/**************************实现函数********************************************
*函数原型:		uint8_t IICreadByte(uint8_t dev, uint8_t reg, uint8_t *data)
*功  能:	    读取指定设备 指定寄存器的一个值
输入	dev  目标设备地址
		reg	   寄存器地址
		*data  读出的数据将要存放的地址
返回   1
*******************************************************************************/ 
uint8_t IICreadByte(uint8_t dev, uint8_t reg, uint8_t *data){
	*data=I2C_ReadOneByte(dev, reg);
    return 1;
}

/**************************实现函数********************************************
*函数原型:		unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data)
*功  能:	    写入指定设备 指定寄存器一个字节
输入	dev  目标设备地址
		reg	   寄存器地址
		data  将要写入的字节
返回   1
*******************************************************************************/ 
unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data){
    return IICwriteBytes(dev, reg, 1, &data);
}

/**************************实现函数********************************************
*函数原型:		uint8_t IICwriteBits(uint8_t dev,uint8_t reg,uint8_t bitStart,uint8_t length,uint8_t data)
*功  能:	    读 修改 写 指定设备 指定寄存器一个字节 中的多个位
输入	dev  目标设备地址
		reg	   寄存器地址
		bitStart  目标字节的起始位
		length   位长度
		data    存放改变目标字节位的值
返回   成功 为1 
 		失败为0
*******************************************************************************/ 
uint8_t IICwriteBits(uint8_t dev,uint8_t reg,uint8_t bitStart,uint8_t length,uint8_t data)
{

    uint8_t b;
    if (IICreadByte(dev, reg, &b) != 0) {
        uint8_t mask = (0xFF << (bitStart + 1)) | 0xFF >> ((8 - bitStart) + length - 1);
        data <<= (8 - length);
        data >>= (7 - bitStart);
        b &= mask;
        b |= data;
        return IICwriteByte(dev, reg, b);
    } else {
        return 0;
    }
}

/**************************实现函数********************************************
*函数原型:		uint8_t IICwriteBit(uint8_t dev, uint8_t reg, uint8_t bitNum, uint8_t data)
*功  能:	    读 修改 写 指定设备 指定寄存器一个字节 中的1个位
输入	dev  目标设备地址
		reg	   寄存器地址
		bitNum  要修改目标字节的bitNum位
		data  为0 时,目标位将被清0 否则将被置位
返回   成功 为1 
 		   失败为0
*******************************************************************************/ 
uint8_t IICwriteBit(uint8_t dev, uint8_t reg, uint8_t bitNum, uint8_t data){
    uint8_t b;
    
    IICreadByte(dev, reg, &b);
    b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
    
    return IICwriteByte(dev, reg, b);
}

//------------------End of File----------------------------

 IIC.h

可以自己修改这里面的配置部分,都是可以自定义的

#ifndef __IIC_H
#define __IIC_H

#include "stm32f10x.h"

#define SCLK_GPIO_PORT GPIOB               /* GPIO端口 */
#define SCLK_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define SCLK_GPIO_PIN GPIO_Pin_8           /* 连接到SCL时钟线的GPIO */

#define DATA_GPIO_PORT GPIOB               /* GPIO端口 */
#define DATA_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define DATA_GPIO_PIN GPIO_Pin_9           /* 连接到SCL时钟线的GPIO */

驱动接口,GPIO模拟IIC
// SCL-->PB9
// SDA-->PB8
// #define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}
// #define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;}

// IO操作函数
#define IIC_SCL_0 digitalLo(SCLK_GPIO_PORT, SCLK_GPIO_PIN) // digitalLo
#define IIC_SCL_1 digitalHi(SCLK_GPIO_PORT, SCLK_GPIO_PIN) // digitalHi
#define IIC_SDA_0 digitalLo(DATA_GPIO_PORT, DATA_GPIO_PIN)
#define IIC_SDA_1 digitalHi(DATA_GPIO_PORT, DATA_GPIO_PIN)
#define READ_SDA GPIO_ReadInputDataBit(DATA_GPIO_PORT, DATA_GPIO_PIN)

// IIC所有操作函数
void IIC_Init(void);                      // 初始化IIC的IO口
void IIC_Start(void);                     // 发送IIC开始信号
void IIC_Stop(void);                      // 发送IIC停止信号
void IIC_Send_Byte(uint8_t txd);          // IIC发送一个字节
uint8_t IIC_Read_Byte(unsigned char ack); // IIC读取一个字节
uint8_t IIC_Wait_Ack(void);               // IIC等待ACK信号
void IIC_Ack(void);                       // IIC发送ACK信号
void IIC_NAck(void);                      // IIC不发送ACK信号

void IIC_Write_One_Byte(uint8_t daddr, uint8_t addr, uint8_t data);
uint8_t IIC_Read_One_Byte(uint8_t daddr, uint8_t addr);
unsigned char I2C_Readkey(unsigned char I2C_Addr);

unsigned char I2C_ReadOneByte(unsigned char I2C_Addr, unsigned char addr);
unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data);
uint8_t IICwriteBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t *data);
uint8_t IICwriteBits(uint8_t dev, uint8_t reg, uint8_t bitStart, uint8_t length, uint8_t data);
uint8_t IICwriteBit(uint8_t dev, uint8_t reg, uint8_t bitNum, uint8_t data);
uint8_t IICreadBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t *data);

#endif

//------------------End of File----------------------------

2.max7219部分

这部分主要用到了一个模拟 SPI 通信 ,这里我们直接用来两个模拟的SPI来独立控制设备,更加方便和精确

max7219.c

#include "stm32f10x.h"
#include "max719.h"
#include "delay.h"

uint8_t m_Intensity = 1; // LED亮度

// SPI1 读写一个字节
void SPI1_ReadWriteByte(uint8_t address, uint8_t data)
{
   uint8_t i;
    // 发送地址字节
    for (i = 0; i < 8; i++) {
        // 设置DIN引脚数据位-写入数据
        if (address & 0x80) {
            GPIO_SetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
        } else {
            GPIO_ResetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
        }
        // 产生时钟脉冲 1-0
        GPIO_SetBits(MAX7219_CLK_PORT, MAX7219_CLK_PIN);
        delay_us(1); // 延迟1微秒
        GPIO_ResetBits(MAX7219_CLK_PORT, MAX7219_CLK_PIN);
        delay_us(1); // 延迟1微秒
        address <<= 1;
    }

    // 发送数据字节
    for (i = 0; i < 8; i++) {
        // 设置DIN引脚数据位
        if (data & 0x80) {
            GPIO_SetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
        } else {
            GPIO_ResetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
        }
        // 产生时钟脉冲 1-0
        GPIO_SetBits(MAX7219_CLK_PORT, MAX7219_CLK_PIN);
        delay_us(1); // 延迟1微秒
        GPIO_ResetBits(MAX7219_CLK_PORT, MAX7219_CLK_PIN);
        delay_us(1); // 延迟1微秒
        data <<= 1;
    }
}
// SPI1 读写一个字节
void SPI2_ReadWriteByte(uint8_t address, uint8_t data)
{
   uint8_t i;

    // 发送地址字节
    for (i = 0; i < 8; i++) {
        // 设置DIN引脚数据位-写入数据
        if (address & 0x80) {
            GPIO_SetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
        } else {
            GPIO_ResetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
        }
        // 产生时钟脉冲 1-0
        GPIO_SetBits(MAX7219_CLK2_PORT, MAX7219_CLK2_PIN);
        delay_us(1); // 延迟1微秒
        GPIO_ResetBits(MAX7219_CLK2_PORT, MAX7219_CLK2_PIN);
        delay_us(1); // 延迟1微秒
        address <<= 1;
    }

    // 发送数据字节
    for (i = 0; i < 8; i++) {
        // 设置DIN引脚数据位
        if (data & 0x80) {
            GPIO_SetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
        } else {
            GPIO_ResetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
        }
        // 产生时钟脉冲 1-0
        GPIO_SetBits(MAX7219_CLK2_PORT, MAX7219_CLK2_PIN);
        delay_us(1); // 延迟1微秒
        GPIO_ResetBits(MAX7219_CLK2_PORT, MAX7219_CLK2_PIN);
        delay_us(1); // 延迟1微秒
        data <<= 1;
    }
}
//写数据*********************************************************************
void MAX7219_Write1(uint8_t addr, uint8_t dat)
{
    MAX7219_CS1_0;
    SPI1_ReadWriteByte(addr,dat);
    MAX7219_CS1_1;
}
//写数据*********************************************************************
void MAX7219_Write2(uint8_t addr, uint8_t dat)
{
    MAX7219_CS2_0;
    SPI2_ReadWriteByte(addr,dat);
    MAX7219_CS2_1;
}
/*
功能:初始化Max7219
参数:-
介绍:有需求可自己调参,一般不需改动
*/
void MAX7219_Init(void)
{
    
		MX_SPI1_Init();	
		MX_SPI2_Init();
    // 初始化 MAX7219
    MAX7219_Write1(0x09, 0x00);        // 译码方式:BCD码
    MAX7219_Write1(0x0a, m_Intensity); // 亮度
    MAX7219_Write1(0x0b, 0x07);        // 扫描界限:8个数码管显示
    MAX7219_Write1(0x0c, 0x01);        // 掉电模式:0,普通模式:1
    MAX7219_Write1(0x0f, 0x00);        // 显示测试:1;测试结束,正常显示:0
		
    MAX7219_Write2(0x09, 0x00);        // 译码方式:BCD码
    MAX7219_Write2(0x0a, m_Intensity); // 亮度
    MAX7219_Write2(0x0b, 0x07);        // 扫描界限:8个数码管显示
    MAX7219_Write2(0x0c, 0x01);        // 掉电模式:0,普通模式:1
    MAX7219_Write2(0x0f, 0x00);        // 显示测试:1;测试结束,正常显示:0
}

void MAX7219_IntensityAdd(void)
{
    if (m_Intensity < 0x0F)
    {
        m_Intensity++;
        MAX7219_Write1(0x0a, m_Intensity);
        MAX7219_Write2(0x0a, m_Intensity);
    }
}

void MAX7219_IntensityMinus(void)
{
    if (m_Intensity > 0)
    {
        m_Intensity--;
        MAX7219_Write1(0x0a, m_Intensity);
        MAX7219_Write2(0x0a, m_Intensity);
    }
}

void MAX7219_ShowMatrix(uint8_t M1[8][8], uint8_t M2[8][8])
{
    uint8_t i, j, dat;
    
    // 更新第一个 MAX7219
    for (i = 0; i < 8; i++)
    {
        dat = (M1[i][0] << 7) | (M1[i][1] << 6) | (M1[i][2] << 5) | (M1[i][3] << 4) | (M1[i][4] << 3) | (M1[i][5] << 2) | (M1[i][6] << 1) | (M1[i][7] << 0);
        MAX7219_Write1(i+1 , dat); // 更新第一个模块
    }
    // 更新第二个 MAX7219
    for (i = 0; i < 8; i++)
    {
        dat = (M2[i][0] << 7) | (M2[i][1] << 6) | (M2[i][2] << 5) | (M2[i][3] << 4) | (M2[i][4] << 3) | (M2[i][5] << 2) | (M2[i][6] << 1) | (M2[i][7] << 0);
        MAX7219_Write2(i+1 , dat); // 更新第二个模块
    }
}

void MX_SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
	

    // 配置CS引脚为推挽输出模式
    GPIO_InitStructure.GPIO_Pin = MAX7219_CS1_PIN ;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(MAX7219_CS1_PORT, &GPIO_InitStructure);

    // 配置CLK引脚为推挽输出模式
    GPIO_InitStructure.GPIO_Pin = MAX7219_CLK_PIN;
    GPIO_Init(MAX7219_CLK_PORT, &GPIO_InitStructure);

    // 配置DIN引脚为推挽输出模式-Mosi
    GPIO_InitStructure.GPIO_Pin = MAX7219_DIN_PIN;
    GPIO_Init(MAX7219_DIN_PORT, &GPIO_InitStructure);

}

void MX_SPI2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
	
	  // 配置CS引脚为推挽输出模式
    GPIO_InitStructure.GPIO_Pin =  MAX7219_CS2_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(MAX7219_CS2_PORT, &GPIO_InitStructure);

    // 配置CLK引脚为推挽输出模式
    GPIO_InitStructure.GPIO_Pin = MAX7219_CLK2_PIN;
    GPIO_Init(MAX7219_CLK2_PORT, &GPIO_InitStructure);

    // 配置DIN引脚为推挽输出模式-Mosi
    GPIO_InitStructure.GPIO_Pin = MAX7219_DIN2_PIN;
    GPIO_Init(MAX7219_DIN2_PORT, &GPIO_InitStructure);



}

max7219.h

这里的 IO 口也可以自定义

#ifndef __SPI_H__
#define __SPI_H__

#include "stm32f10x.h"
//模拟SPI,如有需求自己改动即可
#define MAX7219_CS1_PIN GPIO_Pin_4
#define MAX7219_CS1_PORT GPIOA

#define MAX7219_CLK_PIN GPIO_Pin_5
#define MAX7219_CLK_PORT GPIOA

#define MAX7219_DIN_PIN GPIO_Pin_6
#define MAX7219_DIN_PORT GPIOA

#define MAX7219_CS2_PIN GPIO_Pin_4
#define MAX7219_CS2_PORT GPIOB

#define MAX7219_CLK2_PIN GPIO_Pin_5
#define MAX7219_CLK2_PORT GPIOB

#define MAX7219_DIN2_PIN GPIO_Pin_6
#define MAX7219_DIN2_PORT GPIOB


#define     MAX7219_CS1_0     GPIO_ResetBits(MAX7219_CS1_PORT, MAX7219_CS1_PIN)
#define     MAX7219_CS1_1     GPIO_SetBits(MAX7219_CS1_PORT, MAX7219_CS1_PIN)

#define     MAX7219_CS2_0     GPIO_ResetBits(MAX7219_CS2_PORT, MAX7219_CS2_PIN)
#define     MAX7219_CS2_1     GPIO_SetBits(MAX7219_CS2_PORT, MAX7219_CS2_PIN)

void MAX7219_Init(void);
void MX_SPI1_Init(void);
void MX_SPI2_Init(void);

void MAX7219_ShowMatrix(uint8_t M1[8][8], uint8_t M2[8][8]);
void MAX7219_IntensityMinus(void);
void MAX7219_IntensityAdd(void);
// SPI1 读写一个字节
void SPI1_ReadWriteByte(uint8_t address, uint8_t data);

#endif /* __SPI_H__ */

 3.主代码部分

main.c

实现设备的初始化和数据初始

#include "git.h"

// 软件定时器设定
static Timer task1_id;
static Timer task2_id;
static Timer task3_id;
extern u8 time25ms;
extern uint8_t m_Mat1[8][8], m_Mat2[8][8];

// 获取全局变量
const char *topics[] = {S_TOPIC_NAME};

// 硬件初始化
void Hardware_Init(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级

    delay_init(); // 延时函数初始化
    GENERAL_TIM_Init(TIM_4, 0, 1);
    Usart1_Init(115200); // 串口1初始化为115200

    Usart3_Init(115200); // 串口3,驱动ESP8266用
    System_PB34_setIO();
    LED_Init();
    Key_GPIO_Config(); // key
    MAX7219_Init();    // MAX719
    Matrix_DispNumberOrLetterByIdx(m_Mat1, 23);
    Matrix_DispNumberOrLetterByIdx(m_Mat2, 25);
    MAX7219_ShowMatrix(m_Mat1, m_Mat2);
	
		IIC_Init();
    MPU6050_initialize(); // 陀螺仪
	
		printf("okn");
#if OLED                  // OLED文件存在
    OLED_Init();
    OLED_ColorTurn(0);   // 0正常显示,1 反色显示
    OLED_DisplayTurn(0); // 0正常显示 1 屏幕翻转显示
#endif
    while (Reset_Threshole_Value(&threshold_value_init, &device_state_init) != MY_SUCCESSFUL)
        delay_ms(5); // 初始化阈值

#if OLED // OLED文件存在
    OLED_Clear();
#endif
}

// 任务1
void task1(void)
{
    // 1秒计算器
    Automation_Close();
		
}
// 任务2
void task2(void)
{
    Read_Data(&Data_init);   // 更新传感器数据
    Update_device_massage(); // 更新设备

    State = ~State;
	
}

// 软件初始化
void SoftWare_Init(void)
{
    // 定时器初始化
    timer_init(&task1_id, task1, 980, 1);           // 1s 执行一次
    timer_init(&task2_id, task2, 50, 1); // (git里面设置) ms执行一次


    timer_start(&task1_id);
    timer_start(&task2_id);

}
// 主函数
int main(void)
{

    unsigned char *dataPtr = NULL;
	  SoftWare_Init(); // 软件初始化
    Hardware_Init(); // 硬件初始化


    // 启动提示
    Data_init.Beep = 100;

    TIM_Cmd(TIM4, ENABLE); // 使能计数器
    while (1) {

        // 线程
        timer_loop(); // 定时器执行

#if KEY_OPEN
        // 按键监测
        if (time25ms == MY_TRUE) {
            Check_Key_ON_OFF();
            time25ms = MY_FALSE;
        }
#endif
    }
}

 代码主要实现部分

// 更新设备状态
mySta Update_device_massage()
{
	// 模式
	if (m_Mode == 0)
	{
		Matrix_DispNumberOrLetterByIdx(m_Mat1, m_DropDelay);
		Matrix_DispNumberOrLetterByIdx(m_Mat2, Data_init.Wait_Time); // T
	}
	else if (m_Mode == 1) // 漏沙模式
	{
		m_Moved = Update_Matrix();
		
		if (m_FrameCounter >=1000/Data_init.Time *m_DropDelay || (m_DropDelay == 21 && m_FrameCounter >= 1000/Data_init.Time))
		{
			if (m_Dropped){
				m_FrameCounter = 0;
			}
		}
		if (m_DropDelay == 21 && m_Moved == 0 && m_Dropped == 0) // 漏沙循环
		{
			if( Data_init.ball_num == 60){
				Data_init.ball_num =0;
			}
			if (Is_Matrix_Empty(m_Mat1) && m_Rotation == 0)
			{
				Matrix_Fill(m_Mat1);
				m_Mat1[0][0] = 0;
				m_Mat1[0][1] = 0;
				m_Mat1[1][0] = 0;
				m_Mat1[1][1] = 0;
				Matrix_Clear(m_Mat2);
			}
			else if (Is_Matrix_Empty(m_Mat2) && m_Rotation == 180)
			{
				Matrix_Fill(m_Mat2);
				m_Mat2[0][0] = 0;
				m_Mat2[0][1] = 0;
				m_Mat2[1][0] = 0;
				m_Mat2[1][1] = 0;
				Matrix_Clear(m_Mat1);
			}
		}
		else if (Data_init.ball_num >= 60 && m_Moved == 0 && m_Dropped == 0 && (m_Rotation == 0 || m_Rotation == 180)) // 漏沙结束后切换到模式2
		{
			Data_init.Beep = 500;
			m_FrameCounter = 0;
			m_Mode = 2;
		}
	}
	else if (m_Mode == 2) // 待机眨眼动画
	{
		if (m_FrameCounter >= 1000 / 66 * m_BlinkDelay)
		{
			Blink();
			m_BlinkDelay = rand() % 5 + 5;
			m_FrameCounter = 0;
			
		}
		else{
			OpenEyes();
		}
			
	}
	if(Data_init.ball_num == 60 && m_DropDelay != 21){
		Data_init.Beep = 25;
	}

	// 循环 / 定时
	if(Data_init.Flage == 1){
		m_DropDelay = 21;
	}else{
		m_DropDelay = 23;
	}
	MAX7219_ShowMatrix(m_Mat1, m_Mat2);
	m_FrameCounter++;

	return MY_SUCCESSFUL;
}

四 程序说明

按键说明

按键有长短按模式
长按可以切换模拟,短按可以设置记时长 时长为 1 - 20 min可调

1.沙漏模式

以下图为例:上面为无穷大 表示循环沙漏模式 下面的 1 表示定时时间为1 min

2.定时模式

以下图为例:上面为爱心 表示定时沙漏模式 下面的 1 表示定时时间为1 min,时间到了之后会主动蜂鸣器报警

五 参考

Arduino程序设计(十)8×8 共阴极LED点阵显示(MAX7219)https://blog.csdn.net/weixin_44887565/article/details/132803275?ops_request_misc=%257B%2522request%255Fid%2522%253A%25223b76ef48626591536727ad003b02259d%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=3b76ef48626591536727ad003b02259d&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132803275-null-null.142^v101^pc_search_result_base9&utm_term=max7219%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187https://blog.csdn.net/weixin_44887565/article/details/132803275?ops_request_misc=%257B%2522request%255Fid%2522%253A%25223b76ef48626591536727ad003b02259d%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=3b76ef48626591536727ad003b02259d&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132803275-null-null.142^v101^pc_search_result_base9&utm_term=max7219%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187https://blog.csdn.net/weixin_44887565/article/details/132803275?ops_request_misc=%257B%2522request%255Fid%2522%253A%25223b76ef48626591536727ad003b02259d%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=3b76ef48626591536727ad003b02259d&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132803275-null-null.142^v101^pc_search_result_base9&utm_term=max7219%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187https://blog.csdn.net/weixin_44887565/article/details/132803275?ops_request_misc=%257B%2522request%255Fid%2522%253A%25223b76ef48626591536727ad003b02259d%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=3b76ef48626591536727ad003b02259d&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132803275-null-null.142^v101^pc_search_result_base9&utm_term=max7219%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187MPU-6050详解https://blog.csdn.net/qq_30150579/article/details/136151474?ops_request_misc=%257B%2522request%255Fid%2522%253A%25224cc25091c4627d650c9a33a84e1baf9c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=4cc25091c4627d650c9a33a84e1baf9c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-136151474-null-null.142^v101^pc_search_result_base9&utm_term=MPU-6050%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187https://blog.csdn.net/qq_30150579/article/details/136151474?ops_request_misc=%257B%2522request%255Fid%2522%253A%25224cc25091c4627d650c9a33a84e1baf9c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=4cc25091c4627d650c9a33a84e1baf9c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-136151474-null-null.142^v101^pc_search_result_base9&utm_term=MPU-6050%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187https://blog.csdn.net/qq_30150579/article/details/136151474?ops_request_misc=%257B%2522request%255Fid%2522%253A%25224cc25091c4627d650c9a33a84e1baf9c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=4cc25091c4627d650c9a33a84e1baf9c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-136151474-null-null.142^v101^pc_search_result_base9&utm_term=MPU-6050%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187https://blog.csdn.net/qq_30150579/article/details/136151474?ops_request_misc=%257B%2522request%255Fid%2522%253A%25224cc25091c4627d650c9a33a84e1baf9c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=4cc25091c4627d650c9a33a84e1baf9c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-136151474-null-null.142^v101^pc_search_result_base9&utm_term=MPU-6050%E6%A8%A1%E5%9D%97&spm=1018.2226.3001.4187

联系方式 微信号:13648103287

  • 联系方式.docx
    下载
意法半导体

意法半导体

意法半导体(ST)集团于1987年6月成立,是由意大利的SGS微电子公司和法国Thomson半导体公司合并而成。1998年5月,SGS-THOMSON Microelectronics将公司名称改为意法半导体有限公司。意法半导体是世界最大的半导体公司之一,公司销售收入在半导体工业五大高速增长市场之间分布均衡(五大市场占2007年销售收入的百分比):通信(35%),消费(17%),计算机(16%),汽车(16%),工业(16%)。 据最新的工业统计数据,意法半导体是全球第五大半导体厂商,在很多市场居世界领先水平。例如,意法半导体是世界第一大专用模拟芯片和电源转换芯片制造商,世界第一大工业半导体和机顶盒芯片供应商,而且在分立器件、手机相机模块和车用集成电路领域居世界前列.

意法半导体(ST)集团于1987年6月成立,是由意大利的SGS微电子公司和法国Thomson半导体公司合并而成。1998年5月,SGS-THOMSON Microelectronics将公司名称改为意法半导体有限公司。意法半导体是世界最大的半导体公司之一,公司销售收入在半导体工业五大高速增长市场之间分布均衡(五大市场占2007年销售收入的百分比):通信(35%),消费(17%),计算机(16%),汽车(16%),工业(16%)。 据最新的工业统计数据,意法半导体是全球第五大半导体厂商,在很多市场居世界领先水平。例如,意法半导体是世界第一大专用模拟芯片和电源转换芯片制造商,世界第一大工业半导体和机顶盒芯片供应商,而且在分立器件、手机相机模块和车用集成电路领域居世界前列.收起

查看更多
点赞
收藏
评论
分享
加入交流群
举报

相关推荐

方案定制

去合作
方案开发定制化,2000+方案商即时响应!

方案定制,程序设计方案、单片机程序设计与讲解、APP定制开发。本公众号致力于向读者传递关于程序设计和开发的相关知识,并分享一些关于软件开发的最佳实践。如果您有什么问题或建议,请随时联系我们。我们将竭诚为您服务