前言
最近看到一款電子沙漏,感覺還挺好玩的,然后正好要準(zhǔn)備過年了,閑工夫比較多,那咱也來湊湊熱鬧,不過看了開源廣場,有個(gè)STM32版本的,但是不是STM32F103版本的,所以我在他的查考了他的代碼后自己改寫。
關(guān)注微信公眾號(hào)--星之援工作室 發(fā)送關(guān)鍵字(電子沙漏)
代碼含重要注釋,開源,可自行移植
演示視頻
stm32版本 電子沙漏
一 材料準(zhǔn)備
1.開發(fā)板
2.MPU-6050模塊
MPU6050 X 1:反饋方位
3.MAX7219點(diǎn)陣模塊
MAX7219點(diǎn)陣模塊 X 2:實(shí)現(xiàn)沙漏效果
4.按鍵模塊
按鍵 X 3:實(shí)現(xiàn)按鍵功能
二 引腳使用
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部分
這邊部分 我們主要用到了一個(gè)模擬的IIC通信,并且MPU6050的其他配置函數(shù),都是由現(xiàn)成的,我們只要會(huì)讀代碼就好
?IIC.c
#include "IIC.h"
#include "delay.h"
#include "stdio.h"
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: void IIC_Init(void)
*功 能: 初始化I2C對應(yīng)的接口引腳。
*******************************************************************************/
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端口為輸出態(tài)
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端口為輸入態(tài)
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
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: void IIC_Start(void)
*功 能: 產(chǎn)生IIC起始信號(hào)
*******************************************************************************/
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總線,準(zhǔn)備發(fā)送或接收數(shù)據(jù)
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: void IIC_Stop(void)
*功 能: //產(chǎn)生IIC停止信號(hào)
*******************************************************************************/
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;//發(fā)送I2C總線結(jié)束信號(hào)
delay_us(4);
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IIC_Wait_Ack(void)
*功 能: 等待應(yīng)答信號(hào)到來
//返回值:1,接收應(yīng)答失敗
// 0,接收應(yīng)答成功
*******************************************************************************/
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN(); //SDA設(shè)置為輸入
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;//時(shí)鐘輸出0
return 0;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: void IIC_Ack(void)
*功 能: 產(chǎn)生ACK應(yīng)答
*******************************************************************************/
void IIC_Ack(void)
{
IIC_SCL_0;
SDA_OUT();
IIC_SDA_0;
delay_us(1);
IIC_SCL_1;
delay_us(1);
IIC_SCL_0;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: void IIC_NAck(void)
*功 能: 產(chǎn)生NACK應(yīng)答
*******************************************************************************/
void IIC_NAck(void)
{
IIC_SCL_0;
SDA_OUT();
IIC_SDA_1;
delay_us(1);
IIC_SCL_1;
delay_us(1);
IIC_SCL_0;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: void IIC_Send_Byte(uint8_t txd)
*功 能: IIC發(fā)送一個(gè)字節(jié)
*******************************************************************************/
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL_0;//拉低時(shí)鐘開始數(shù)據(jù)傳輸
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);
}
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IIC_Read_Byte(unsigned char ack)
*功 能: //讀1個(gè)字節(jié),ack=1時(shí),發(fā)送ACK,ack=0,發(fā)送nACK
*******************************************************************************/
uint8_t IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA設(shè)置為輸入
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(); //發(fā)送ACK
}
else{
IIC_NAck();//發(fā)送nACK
}
return receive;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr)
*功 能: 讀取指定設(shè)備 指定寄存器的一個(gè)值
輸入 I2C_Addr 目標(biāo)設(shè)備地址
addr 寄存器地址
返回 讀出來的值
*******************************************************************************/
unsigned char I2C_ReadOneByte(unsigned char I2C_Addr,unsigned char addr)
{
unsigned char res=0;
IIC_Start();
IIC_Send_Byte(I2C_Addr); //發(fā)送寫命令
res++;
IIC_Wait_Ack();
IIC_Send_Byte(addr); res++; //發(fā)送地址
IIC_Wait_Ack();
//IIC_Stop();//產(chǎn)生一個(gè)停止條件
IIC_Start();
IIC_Send_Byte(I2C_Addr+1); res++; //進(jìn)入接收模式
IIC_Wait_Ack();
res=IIC_Read_Byte(0);
IIC_Stop();//產(chǎn)生一個(gè)停止條件
return res;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IICreadBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t *data)
*功 能: 讀取指定設(shè)備 指定寄存器的 length個(gè)值
輸入 dev 目標(biāo)設(shè)備地址
reg 寄存器地址
length 要讀的字節(jié)數(shù)
*data 讀出的數(shù)據(jù)將要存放的指針
返回 讀出來的字節(jié)數(shù)量
*******************************************************************************/
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); //發(fā)送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(reg); //發(fā)送地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(dev+1); //進(jìn)入接收模式
IIC_Wait_Ack();
for(count=0;count<length;count++){
if(count!=(length-1)){
temp = IIC_Read_Byte(1); //帶ACK的讀數(shù)據(jù)
}
else{
temp = IIC_Read_Byte(0); //最后一個(gè)字節(jié)NACK
}
data[count] = temp;
}
IIC_Stop();//產(chǎn)生一個(gè)停止條件
return count;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IICwriteBytes(uint8_t dev, uint8_t reg, uint8_t length, uint8_t* data)
*功 能: 將多個(gè)字節(jié)寫入指定設(shè)備 指定寄存器
輸入 dev 目標(biāo)設(shè)備地址
reg 寄存器地址
length 要寫的字節(jié)數(shù)
*data 將要寫的數(shù)據(jù)的首地址
返回 返回是否成功
*******************************************************************************/
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); //發(fā)送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(reg); //發(fā)送地址
IIC_Wait_Ack();
for(count=0;count<length;count++){
IIC_Send_Byte(data[count]);
IIC_Wait_Ack();
}
IIC_Stop();//產(chǎn)生一個(gè)停止條件
return 1; //status == 0;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IICreadByte(uint8_t dev, uint8_t reg, uint8_t *data)
*功 能: 讀取指定設(shè)備 指定寄存器的一個(gè)值
輸入 dev 目標(biāo)設(shè)備地址
reg 寄存器地址
*data 讀出的數(shù)據(jù)將要存放的地址
返回 1
*******************************************************************************/
uint8_t IICreadByte(uint8_t dev, uint8_t reg, uint8_t *data){
*data=I2C_ReadOneByte(dev, reg);
return 1;
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data)
*功 能: 寫入指定設(shè)備 指定寄存器一個(gè)字節(jié)
輸入 dev 目標(biāo)設(shè)備地址
reg 寄存器地址
data 將要寫入的字節(jié)
返回 1
*******************************************************************************/
unsigned char IICwriteByte(unsigned char dev, unsigned char reg, unsigned char data){
return IICwriteBytes(dev, reg, 1, &data);
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IICwriteBits(uint8_t dev,uint8_t reg,uint8_t bitStart,uint8_t length,uint8_t data)
*功 能: 讀 修改 寫 指定設(shè)備 指定寄存器一個(gè)字節(jié) 中的多個(gè)位
輸入 dev 目標(biāo)設(shè)備地址
reg 寄存器地址
bitStart 目標(biāo)字節(jié)的起始位
length 位長度
data 存放改變目標(biāo)字節(jié)位的值
返回 成功 為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;
}
}
/**************************實(shí)現(xiàn)函數(shù)********************************************
*函數(shù)原型: uint8_t IICwriteBit(uint8_t dev, uint8_t reg, uint8_t bitNum, uint8_t data)
*功 能: 讀 修改 寫 指定設(shè)備 指定寄存器一個(gè)字節(jié) 中的1個(gè)位
輸入 dev 目標(biāo)設(shè)備地址
reg 寄存器地址
bitNum 要修改目標(biāo)字節(jié)的bitNum位
data 為0 時(shí),目標(biāo)位將被清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端口時(shí)鐘 */
#define SCLK_GPIO_PIN GPIO_Pin_8 /* 連接到SCL時(shí)鐘線的GPIO */
#define DATA_GPIO_PORT GPIOB /* GPIO端口 */
#define DATA_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口時(shí)鐘 */
#define DATA_GPIO_PIN GPIO_Pin_9 /* 連接到SCL時(shí)鐘線的GPIO */
驅(qū)動(dòng)接口,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操作函數(shù)
#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所有操作函數(shù)
void IIC_Init(void); // 初始化IIC的IO口
void IIC_Start(void); // 發(fā)送IIC開始信號(hào)
void IIC_Stop(void); // 發(fā)送IIC停止信號(hào)
void IIC_Send_Byte(uint8_t txd); // IIC發(fā)送一個(gè)字節(jié)
uint8_t IIC_Read_Byte(unsigned char ack); // IIC讀取一個(gè)字節(jié)
uint8_t IIC_Wait_Ack(void); // IIC等待ACK信號(hào)
void IIC_Ack(void); // IIC發(fā)送ACK信號(hào)
void IIC_NAck(void); // IIC不發(fā)送ACK信號(hào)
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部分
這部分主要用到了一個(gè)模擬 SPI 通信 ,這里我們直接用來兩個(gè)模擬的SPI來獨(dú)立控制設(shè)備,更加方便和精確
max7219.c
#include "stm32f10x.h"
#include "max719.h"
#include "delay.h"
uint8_t m_Intensity = 1; // LED亮度
// SPI1 讀寫一個(gè)字節(jié)
void SPI1_ReadWriteByte(uint8_t address, uint8_t data)
{
uint8_t i;
// 發(fā)送地址字節(jié)
for (i = 0; i < 8; i++) {
// 設(shè)置DIN引腳數(shù)據(jù)位-寫入數(shù)據(jù)
if (address & 0x80) {
GPIO_SetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
} else {
GPIO_ResetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
}
// 產(chǎn)生時(shí)鐘脈沖 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;
}
// 發(fā)送數(shù)據(jù)字節(jié)
for (i = 0; i < 8; i++) {
// 設(shè)置DIN引腳數(shù)據(jù)位
if (data & 0x80) {
GPIO_SetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
} else {
GPIO_ResetBits(MAX7219_DIN_PORT, MAX7219_DIN_PIN);
}
// 產(chǎn)生時(shí)鐘脈沖 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 讀寫一個(gè)字節(jié)
void SPI2_ReadWriteByte(uint8_t address, uint8_t data)
{
uint8_t i;
// 發(fā)送地址字節(jié)
for (i = 0; i < 8; i++) {
// 設(shè)置DIN引腳數(shù)據(jù)位-寫入數(shù)據(jù)
if (address & 0x80) {
GPIO_SetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
} else {
GPIO_ResetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
}
// 產(chǎn)生時(shí)鐘脈沖 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;
}
// 發(fā)送數(shù)據(jù)字節(jié)
for (i = 0; i < 8; i++) {
// 設(shè)置DIN引腳數(shù)據(jù)位
if (data & 0x80) {
GPIO_SetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
} else {
GPIO_ResetBits(MAX7219_DIN2_PORT, MAX7219_DIN2_PIN);
}
// 產(chǎn)生時(shí)鐘脈沖 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;
}
}
//寫數(shù)據(jù)*********************************************************************
void MAX7219_Write1(uint8_t addr, uint8_t dat)
{
MAX7219_CS1_0;
SPI1_ReadWriteByte(addr,dat);
MAX7219_CS1_1;
}
//寫數(shù)據(jù)*********************************************************************
void MAX7219_Write2(uint8_t addr, uint8_t dat)
{
MAX7219_CS2_0;
SPI2_ReadWriteByte(addr,dat);
MAX7219_CS2_1;
}
/*
功能:初始化Max7219
參數(shù):-
介紹:有需求可自己調(diào)參,一般不需改動(dòng)
*/
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個(gè)數(shù)碼管顯示
MAX7219_Write1(0x0c, 0x01); // 掉電模式:0,普通模式:1
MAX7219_Write1(0x0f, 0x00); // 顯示測試:1;測試結(jié)束,正常顯示:0
MAX7219_Write2(0x09, 0x00); // 譯碼方式:BCD碼
MAX7219_Write2(0x0a, m_Intensity); // 亮度
MAX7219_Write2(0x0b, 0x07); // 掃描界限:8個(gè)數(shù)碼管顯示
MAX7219_Write2(0x0c, 0x01); // 掉電模式:0,普通模式:1
MAX7219_Write2(0x0f, 0x00); // 顯示測試:1;測試結(jié)束,正常顯示: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;
// 更新第一個(gè) 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); // 更新第一個(gè)模塊
}
// 更新第二個(gè) 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); // 更新第二個(gè)模塊
}
}
void MX_SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA時(shí)鐘
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時(shí)鐘
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,如有需求自己改動(dòng)即可
#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 讀寫一個(gè)字節(jié)
void SPI1_ReadWriteByte(uint8_t address, uint8_t data);
#endif /* __SPI_H__ */
?3.主代碼部分
main.c
實(shí)現(xiàn)設(shè)備的初始化和數(shù)據(jù)初始
#include "git.h"
// 軟件定時(shí)器設(shè)定
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); // 設(shè)置中斷優(yōu)先級分組為組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
delay_init(); // 延時(shí)函數(shù)初始化
GENERAL_TIM_Init(TIM_4, 0, 1);
Usart1_Init(115200); // 串口1初始化為115200
Usart3_Init(115200); // 串口3,驅(qū)動(dòng)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 屏幕翻轉(zhuǎn)顯示
#endif
while (Reset_Threshole_Value(&threshold_value_init, &device_state_init) != MY_SUCCESSFUL)
delay_ms(5); // 初始化閾值
#if OLED // OLED文件存在
OLED_Clear();
#endif
}
// 任務(wù)1
void task1(void)
{
// 1秒計(jì)算器
Automation_Close();
}
// 任務(wù)2
void task2(void)
{
Read_Data(&Data_init); // 更新傳感器數(shù)據(jù)
Update_device_massage(); // 更新設(shè)備
State = ~State;
}
// 軟件初始化
void SoftWare_Init(void)
{
// 定時(shí)器初始化
timer_init(&task1_id, task1, 980, 1); // 1s 執(zhí)行一次
timer_init(&task2_id, task2, 50, 1); // (git里面設(shè)置) ms執(zhí)行一次
timer_start(&task1_id);
timer_start(&task2_id);
}
// 主函數(shù)
int main(void)
{
unsigned char *dataPtr = NULL;
SoftWare_Init(); // 軟件初始化
Hardware_Init(); // 硬件初始化
// 啟動(dòng)提示
Data_init.Beep = 100;
TIM_Cmd(TIM4, ENABLE); // 使能計(jì)數(shù)器
while (1) {
// 線程
timer_loop(); // 定時(shí)器執(zhí)行
#if KEY_OPEN
// 按鍵監(jiān)測
if (time25ms == MY_TRUE) {
Check_Key_ON_OFF();
time25ms = MY_FALSE;
}
#endif
}
}
?代碼主要實(shí)現(xiàn)部分
// 更新設(shè)備狀態(tài)
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) // 漏沙循環(huán)
{
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)) // 漏沙結(jié)束后切換到模式2
{
Data_init.Beep = 500;
m_FrameCounter = 0;
m_Mode = 2;
}
}
else if (m_Mode == 2) // 待機(jī)眨眼動(dòng)畫
{
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;
}
// 循環(huán) / 定時(shí)
if(Data_init.Flage == 1){
m_DropDelay = 21;
}else{
m_DropDelay = 23;
}
MAX7219_ShowMatrix(m_Mat1, m_Mat2);
m_FrameCounter++;
return MY_SUCCESSFUL;
}
四 程序說明
按鍵說明
按鍵有長短按模式
長按可以切換模擬,短按可以設(shè)置記時(shí)長 時(shí)長為 1 - 20 min可調(diào)
1.沙漏模式
以下圖為例:上面為無窮大 表示循環(huán)沙漏模式 下面的 1 表示定時(shí)時(shí)間為1 min
2.定時(shí)模式
以下圖為例:上面為愛心 表示定時(shí)沙漏模式 下面的 1 表示定時(shí)時(shí)間為1 min,時(shí)間到了之后會(huì)主動(dòng)蜂鳴器報(bào)警
五 參考
Arduino程序設(shè)計(jì)(十)8×8 共陰極LED點(diǎn)陣顯示(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.4187
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.4187
聯(lián)系方式 微信號(hào):13648103287