【2023海光杯】“智能储物柜系统”电控部分

简单说明

在代码实现部分会给出设计理念和分析,整体资源可以直接下载压缩包(手机端依然看不到,还是不知道为什么)。

使用设备

按照题目要求需要制作16个储物格,对应16扇门。16扇门的开关可以用矩阵键盘来控制。 在不考虑扩展功能的前提下,该项目的难点在于对16扇门的联控,以及对其响应的时间精度和空间精度的要求。

关于柜体,柜体主体采用亚克力板,柜门采用1mm亚克力板(防止步进电机因为扭力问题导致无法对柜门进行更精准的控制)。

这个项目的电控部分拆分下来大致可以分为四板块:

一、主控板

电控部分采用STM32F103C8T6作为主控板,满足该项目对IO口,时钟及传输等方面的要求。

二、矩阵键盘

4*4矩阵键盘作为信号输入,初始状态下柜门成关闭状态,此时按下按键,对应柜门打开,再一次按下按键,柜门关闭。通过这种方式实现柜门控制指令的产生。

三、PWM驱动

由于该项目中需要进行对16个舵机的联控,将每个舵机直接接入主控板显然是不现实的,也是不高效的。这里采用PCA9685,从而实现I2C转16路PWM,达到对16个舵机进行联控的要求。

四、SG90舵机

SG90舵机,是伺服电机的一种,伺服电机就是带有反馈环节的电机,这种电机可以进行精确的位置控制或者输出较高的扭矩。电机输出轴的位置由内部电位计不断采样测量,并与微控制器(例如STM32,Arduino)设置的目标位置进行比较;根据相应的偏差,控制设备会调整电机输出轴的实际位置,使其与目标位置匹配,这样就形成了闭环控制系统。

电路设计

代码实现

注:这里只给出作者自己编写的部分,SYSTEM文件夹下有关delay,sys和uart的代码采用"正点原子"提供的例程,HAL库请自行导入。文件代码结构如下图:

1. main.c

cpp 复制代码
#include "sys.h"
#include "delay.h"
#include "key_matrix.h"
#include "usart.h"
#include "stm32_pca9685.h"

/*
PCA:
SCL-B6
SDA-B7
VCC
GND
		USB转TTL
			PA9	---- RX
			PA10---- TX
		按键矩阵:
			COL:
				PA1--COL1
				PA2--COL2
				PA3--COL3
				PA4--COL4
			ROW:
				PB12--ROW1
				PB13--ROW2
				PB14--ROW3
				PB15--ROW4
*/

int main(void)
{	
	char d1 = 1,d2 = 1,d3 = 1,d4 = 1,d5 = 1,d6 = 1,d7 = 1,d8 = 1,d9 = 1,d10 = 1,d11 = 1,d12 = 1,d13 = 1,d14 = 1,d15 = 1,d16 = 1;
	//系统功能,设备,硬件初始化
	delay_init();
	NVIC_Configuration();
	USARTX_Init(USART1, 115200);
	KEY_MATRIX_Init();
	PCA_MG9XX_Init(50,60);//初始化舵机驱动
	while(1) 
	{	
		KEY_MATRIX_Scan();
		if(xKEY_MATRIX.res_flag == 1)	
		{
			switch(xKEY_MATRIX.res)
			{
			case 1:
				{
					if(d1 == 0)
					{
						PCA_MG9XX(0,0,60,0,10); 
						d1=1;
					}
					else if(d1 == 1)
					{
						PCA_MG9XX(0,0,147,0,10); 
						d1=0;
					}
				break;
				}
				
			case 2:
				{
					if(d2 == 0)
					{
						PCA_MG9XX(1,0,60,0,10); 
						d2=1;
					}
					else if(d2 == 1)
					{
						PCA_MG9XX(1,0,146,0,10); 
						d2 = 0;
					}
				break;
				}
				
				case 3:
				{
					if(d3 == 0)
					{
						PCA_MG9XX(2,0,60,0,10); 
						d3=1;
					}
					else if(d3 == 1)
					{
						PCA_MG9XX(2,0,146,0,10); 
						d3=0;
					}
				break;
				}
				
				case 4:
				{
					if(d4 == 0)
					{
						PCA_MG9XX(3,0,60,0,10); 
						d4=1;
					}
					else if(d4 == 1)
					{
						PCA_MG9XX(3,0,150,0,10); 
						d4=0;
					}
				break;
				}
				
				case 5:
				{
					if(d5 == 0)
					{
						PCA_MG9XX(4,0,60,0,10); 
						d5=1;
					}
					else if(d5 == 1)
					{
						PCA_MG9XX(4,0,146,0,10); 
						d5=0;
					}
				break;
				}
				
				case 6:
				{
					if(d6 == 0)
					{
						PCA_MG9XX(5,0,60,0,10); 
						d6=1;
					}
					else if(d6 == 1)
					{
						PCA_MG9XX(5,0,146,0,10); 
						d6=0;
					}
				break;
				}
				
				case 7:
				{
					if(d7 == 0)
					{
						PCA_MG9XX(6,0,60,0,10); 
						d7=1;
					}
					else if(d7 == 1)
					{
						PCA_MG9XX(6,0,146,0,10); 
						d7=0;
					}
				break;
				}
				
				case 8:
				{
					if(d8 == 0)
					{
						PCA_MG9XX(7,0,60,0,10); 
						d8=1;
					}
					else if(d8 == 1)
					{
						PCA_MG9XX(7,0,150,0,10); 
						d8=0;
					}
				break;
				}
				
				case 9:
				{
					if(d9 == 0)
					{
						PCA_MG9XX(8,0,60,0,10); 
						d9=1;
					}
					else if(d9 == 1)
					{
						PCA_MG9XX(8,0,146,0,10); 
						d9=0;
					}
				break;
				}
				
				case 10:
				{
					if(d10 == 0)
					{
						PCA_MG9XX(9,0,60,0,10); 
						d10=1;
					}
					else if(d10 == 1)
					{
						PCA_MG9XX(9,0,146,0,10); 
						d10=0;
					}
				break;
				}
				
				case 11:
				{
					if(d11 == 0)
					{
						PCA_MG9XX(10,0,60,0,10); 
						d11=1;
					}
					else if(d11 == 1)
					{
						PCA_MG9XX(10,0,146,0,10); 
						d11=0;
					}
				break;
				}
				
				case 12:
				{
					if(d12 == 0)
					{
						PCA_MG9XX(11,0,60,0,10); 
						d12=1;
					}
					else if(d12 == 1)
					{
						PCA_MG9XX(11,0,146,0,10); 
						d12=0;
					}
				break;
				}
				
				case 13:
				{
					if(d13 == 0)
					{
						PCA_MG9XX(12,0,60,0,10); 
						d13=1;
					}
					else if(d13 == 1)
					{
						PCA_MG9XX(12,0,150,0,10); 
						d13=0;
					}
				break;
				}
				
				case 14:
				{
					if(d14 == 0)
					{
						PCA_MG9XX(13,0,60,0,10); 
						d14=1;
					}
					else if(d14 == 1)
					{
						PCA_MG9XX(13,0,155,0,10); 
						d14=0;
					}
				break;
				}
				
				case 15:
				{
					if(d15 == 0)
					{
						PCA_MG9XX(14,0,60,0,10); 
						d15=1;
					}
					else if(d15 == 1)
					{
						PCA_MG9XX(14,0,155,0,10); 
						d15=0;
					}
				break;
				}
				
				case 16:
				{
					if(d16 == 0)
					{
						PCA_MG9XX(15,0,60,0,10); 
						d16=1;
					}
					else if(d16 == 1)
					{
						PCA_MG9XX(15,0,155,0,10); 
						d16=0;
					}
				break;
				}
			}
//			printf("按下第 %d 行 \t 第 %d 列 \t 第 %d 个",xKEY_MATRIX.res_row,xKEY_MATRIX.res_col,xKEY_MATRIX.res);
		}
			while(xKEY_MATRIX.res_flag == 1)
				KEY_MATRIX_Scan();
	}	  
	
}

2. key_matrix.c

cpp 复制代码
#include "key_matrix.h"

#define KEY_MATRIX_APB2PERIPH  RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB

#define KEY_ROW1_PORT	GPIOB
#define KEY_ROW2_PORT	GPIOB
#define KEY_ROW3_PORT	GPIOB
#define KEY_ROW4_PORT	GPIOB

#define KEY_ROW1_PIN	GPIO_Pin_12
#define KEY_ROW2_PIN	GPIO_Pin_13
#define KEY_ROW3_PIN	GPIO_Pin_14
#define KEY_ROW4_PIN	GPIO_Pin_15

#define KEY_COL1_PORT	GPIOA
#define KEY_COL2_PORT	GPIOA
#define KEY_COL3_PORT	GPIOA
#define KEY_COL4_PORT	GPIOA

#define KEY_COL1_PIN	GPIO_Pin_1
#define KEY_COL2_PIN	GPIO_Pin_2
#define KEY_COL3_PIN	GPIO_Pin_3
#define KEY_COL4_PIN	GPIO_Pin_4

static GPIO_GROUP GPIO_KEY_ROW[4] = {
	{KEY_ROW1_PORT, KEY_ROW1_PIN},
	{KEY_ROW2_PORT, KEY_ROW2_PIN},
	{KEY_ROW3_PORT, KEY_ROW3_PIN},
	{KEY_ROW4_PORT, KEY_ROW4_PIN},
};
static GPIO_GROUP GPIO_KEY_COL[4]= {
	{KEY_COL1_PORT, KEY_COL1_PIN},
	{KEY_COL2_PORT, KEY_COL2_PIN},
	{KEY_COL3_PORT, KEY_COL3_PIN},
	{KEY_COL4_PORT, KEY_COL4_PIN},
};

xKEY_MATRIX_TypeDef xKEY_MATRIX = {0, 0, 0, 0};

static void KEY_ROW_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
static void KEY_COL_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

//控制某行电压
static void Activate_ROW(uint8_t row, uint8_t status);
//按键矩阵全部行的电压全部设置
static void Activate_ALL_ROW(uint8_t status);


/******************************************************************************
 * 函  数: KEY_MATRIX_Init
 * 功  能: 按键矩阵初始化    
 * 参  数: 无
 ******************************************************************************/  
void KEY_MATRIX_Init(void)
{
	u8 i = 0;
	//开启时钟
	RCC_APB2PeriphClockCmd(KEY_MATRIX_APB2PERIPH,ENABLE);	
	
	//配置引脚
	//KEY ROW  	推挽输出电压
	for(i = 0; i < 4; i++) 	KEY_ROW_Init(GPIO_KEY_ROW[i].GPIOx, GPIO_KEY_ROW[i].Pinx);
	
	//KEY COL	下拉输入
	for(i = 0; i < 4; i++)	KEY_COL_Init(GPIO_KEY_COL[i].GPIOx, GPIO_KEY_COL[i].Pinx);
}

/******************************************************************************
 * 函  数: KEY_ROW_Init
 * 功  能: 按键矩阵行引脚初始化    
 * 参  数: 无
 ******************************************************************************/  
void KEY_ROW_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOx, &GPIO_InitStructure);  
}

/******************************************************************************
 * 函  数: KEY_COL_Init
 * 功  能: 按键矩阵列引脚初始化    
 * 参  数: 无
 ******************************************************************************/ 
void KEY_COL_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOx, &GPIO_InitStructure); 
}

/******************************************************************************
 * 函  数: Activate_ROW
 * 功  能: 按键矩阵行电压变化
 * 参  数: 无
 ******************************************************************************/ 
void Activate_ROW(uint8_t row, uint8_t status)
{
	if (status) 	GPIO_SetBits(GPIO_KEY_ROW[row].GPIOx, GPIO_KEY_ROW[row].Pinx);
	else 			GPIO_ResetBits(GPIO_KEY_ROW[row].GPIOx, GPIO_KEY_ROW[row].Pinx);
}

/******************************************************************************
 * 函  数: Activate_ROW
 * 功  能: 按键矩阵全部行电压一起变化
 * 参  数: 无
 ******************************************************************************/ 
void Activate_ALL_ROW(uint8_t status)
{
	int row;
	for (row = 0; row < 4; row++) 		Activate_ROW(row, status);
}

/******************************************************************************
 * 函  数: KEY_MATRIX_Scan
 * 功  能: 从按键矩阵中读取按下按键的位置数据       
 * 参  数: 无
 * 说  明: 调用后,获取到的数据,保存到结构体xKEY_MATRIX中
 ******************************************************************************/  
void KEY_MATRIX_Scan(void)
{
	u8 row = 0,col = 0;
	//所有行电平置零复位
	Activate_ALL_ROW(0);
	//每行电压变化 每次电压变化完检查列是否有读取到电压
	//当读取到的时候即按键按下
	xKEY_MATRIX.res_flag = 0;
	for(row = 0; row < 4 ; row++)
	{
		//行电压变化
		Activate_ROW(row, 1);
		delay_ms(1);
		for(col = 0; col < 4; col++)
		{
			//检测到高电平
			if(GPIO_ReadInputDataBit(GPIO_KEY_COL[col].GPIOx,GPIO_KEY_COL[col].Pinx) == 1)
			{
				// 结构统一
				xKEY_MATRIX.res_flag = 1;
				xKEY_MATRIX.res_row = row + 1;
				xKEY_MATRIX.res_col = col + 1;
				xKEY_MATRIX.res = row * 4 + col + 1;
			} 
		}
		Activate_ROW(row, 0);
	}
	//所有行电平置零复位
	Activate_ALL_ROW(0);
}

3. hey_matrix.h

cpp 复制代码
#ifndef __KEY_MATRIX_H
#define __KEY_MATRIX_H


#include <stm32f10x.h>  
#include <stdio.h>
#include "sys.h"
#include "delay.h"

/*****************************************************************************
 ** 宏定义
****************************************************************************/


/*****************************************************************************
 ** 声明 全局变量
****************************************************************************/


typedef struct {
	GPIO_TypeDef* 	GPIOx;	//所用的GPIO端口
	uint16_t 		Pinx;	//所用的Pin引脚	
}GPIO_GROUP;

typedef struct 
{
	uint8_t 	res_flag;			// 检测结果  0:没有按下	1:有按下
	uint8_t 	res_row;			// 第几行
	uint8_t		res_col;			// 第几列
	uint8_t		res;				// 1-16 	从左到右从上到下
}xKEY_MATRIX_TypeDef;

extern xKEY_MATRIX_TypeDef xKEY_MATRIX;

/*****************************************************************************
 ** 声明  全局函数
****************************************************************************/
void KEY_MATRIX_Init(void);	// 按键矩阵初始化函数

void KEY_MATRIX_Scan(void);	// 按键矩阵扫描函数

#endif

4. stm32_pca9685.c

cpp 复制代码
#include "stm32_pca9685.h"
#include "delay.h"
#include "math.h"

void pca_write(u8 adrr,u8 data)//向PCA写数据,adrrd地址,data数据
{ 
	IIC_Start();
	
	IIC_Send_Byte(pca_adrr);
	IIC_Wait_Ack();
	
	IIC_Send_Byte(adrr);
	IIC_Wait_Ack();
	
	IIC_Send_Byte(data);
	IIC_Wait_Ack();
	
	IIC_Stop();
}

u8 pca_read(u8 adrr)//从PCA读数据
{
	u8 data;
	IIC_Start();
	
	IIC_Send_Byte(pca_adrr);
	IIC_Wait_Ack();
	
	IIC_Send_Byte(adrr);
	IIC_Wait_Ack();
	
	IIC_Start();
	
	IIC_Send_Byte(pca_adrr|0x01);
	IIC_Wait_Ack();
	
	data=IIC_Read_Byte(0);
	IIC_Stop();
	
	return data;
}


void pca_setfreq(float freq)//设置PWM频率
{
		u8 prescale,oldmode,newmode;
		double prescaleval;
		freq *= 0.92; 
		prescaleval = 25000000;
		prescaleval /= 4096;
		prescaleval /= freq;
		prescaleval -= 1;
		prescale =floor(prescaleval + 0.5f);

		oldmode = pca_read(pca_mode1);
	
		newmode = (oldmode&0x7F) | 0x10; // sleep
	
		pca_write(pca_mode1, newmode); // go to sleep
	
		pca_write(pca_pre, prescale); // set the prescaler
	
		pca_write(pca_mode1, oldmode);
		delay_ms(2);
	
		pca_write(pca_mode1, oldmode | 0xa1); 
}

void pca_setpwm(u8 num, u32 on, u32 off)
{
		pca_write(LED0_ON_L+4*num,on);
		pca_write(LED0_ON_H+4*num,on>>8);
		pca_write(LED0_OFF_L+4*num,off);
		pca_write(LED0_OFF_H+4*num,off>>8);
}
/*num:舵机PWM输出引脚0~15,on:PWM上升计数值0~4096,off:PWM下降计数值0~4096
一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时
跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,
off/4096的值就是PWM的占空比。*/

/*
	函数作用:初始化舵机驱动板
	参数:1.PWM频率
		  2.初始化舵机角度
*/
void PCA_MG9XX_Init(float hz,u8 angle)
{
	u32 off=0;
	IIC_Init();
	pca_write(pca_mode1,0x0);
	pca_setfreq(hz);//设置PWM频率
	off=(u32)(145+angle*2.4);
	pca_setpwm(0,0,off);pca_setpwm(1,0,off);pca_setpwm(2,0,off);pca_setpwm(3,0,off);
	pca_setpwm(4,0,off);pca_setpwm(5,0,off);pca_setpwm(6,0,off);pca_setpwm(7,0,off);
	pca_setpwm(8,0,off);pca_setpwm(9,0,off);pca_setpwm(10,0,off);pca_setpwm(11,0,off);
	pca_setpwm(12,0,off);pca_setpwm(13,0,off);pca_setpwm(14,0,off);pca_setpwm(15,0,off);
	delay_ms(500);
}

/*
	函数作用:控制舵机转动;
	参数:1.输出端口,可选0~15;
		  2.起始角度,可选0~180;
		  3.结束角度,可选0~180;
		  4.模式选择,0 表示函数内无延时,调用时需要在函数后另外加延时函数,且不可调速,第五个参数可填任意值;
					  1 表示函数内有延时,调用时不需要在函数后另外加延时函数,且不可调速,第五个参数可填任意值;
					  2 表示速度可调,第五个参数表示速度值;
		  5.速度,可填大于 0 的任意值,填 1 时速度最快,数值越大,速度越小;
	注意事项:模式 0和1 的速度比模式 2 的最大速度大;
*/
void PCA_MG9XX(u8 num,u8 start_angle,u8 end_angle,u8 mode,u8 speed)
{
	u8 i;
	u32 off=0;
	switch(mode)
	{
		case 0:
			off=(u32)(158+end_angle*2.2);
			pca_setpwm(num,0,off);
			break;
		case 1:
			off=(u32)(158+end_angle*2.2);
			pca_setpwm(num,0,off);
			if(end_angle>start_angle){delay_ms((u16)((end_angle-start_angle)*2.7));}
			else{delay_ms((u16)((start_angle-end_angle)*2.7));}
			break;
		case 2:
			if(end_angle>start_angle)
			{
				for(i=start_angle;i<=end_angle;i++)
				{
					off=(u32)(158+i*2.2);
					pca_setpwm(num,0,off);
					delay_ms(2);
					delay_us(speed*250);
				}
			}
			else if(start_angle>end_angle)
			{
				for(i=start_angle;i>=end_angle;i--)
				{
					off=(u32)(158+i*2.2);
					pca_setpwm(num,0,off);
					delay_ms(2);
					delay_us(speed*250);
				}
			}
			break;
	}
}


//----------------
//初始化IIC
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB时钟
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9); 	//PB6,PB7 输出高
}
//产生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总线,准备发送或接收数据 
}	  
//产生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);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 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(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

5. stm32_pca9685.h

cpp 复制代码
#ifndef __STM32PCA9685_H
#define __STM32PCA9685_H	

#include "stm32f10x.h"


#define pca_adrr 0x80

#define pca_mode1 0x0
#define pca_pre 0xFE

#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9

#define jdMIN  115 // minimum
#define jdMAX  590 // maximum
#define jd000  130 //0度对应4096的脉宽计数值
#define jd180  520 //180度对应4096的脉宽计算值



//IO方向设置
 
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

//IO操作函数	 
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA	 
#define READ_SDA   PBin(7 )  //输入SDA 





void pca_write(u8 adrr,u8 data);
u8 pca_read(u8 adrr);
void PCA_MG9XX_Init(float hz,u8 angle);
void pca_setfreq(float freq);
void pca_setpwm(u8 num, u32 on, u32 off);
void PCA_MG9XX(u8 num,u8 start_angle,u8 end_angle,u8 mode,u8 speed);




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


#endif

6. GPIOLIKE51.h

cpp 复制代码
#ifndef __GPIOLIKE51_H
#define __GPIOLIKE51_H	 

///#include <stm32f10x_lib.h>

//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#endif

成品展示

"智能储物柜"成品展示

相关推荐
智者知已应修善业44 分钟前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
智商偏低7 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen8 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森10 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白10 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D11 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术14 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt14 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘15 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang15 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c