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

简单说明

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

使用设备

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

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

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

一、主控板

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

二、矩阵键盘

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

三、PWM驱动

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

四、SG90舵机

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

电路设计

【2023海光杯】“智能储物柜系统”电控部分_第1张图片

代码实现

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

【2023海光杯】“智能储物柜系统”电控部分_第2张图片

1. main.c

#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

#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

#ifndef __KEY_MATRIX_H
#define __KEY_MATRIX_H


#include   
#include 
#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

#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

#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 

#ifndef __GPIOLIKE51_H
#define __GPIOLIKE51_H	 

///#include 

//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<>第五章(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

成品展示

“智能储物柜”成品展示

你可能感兴趣的:(嵌入式,单片机,嵌入式硬件)