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

news2024/12/23 11:15:36

简单说明

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

使用设备

        按照题目要求需要制作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

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

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

成品展示

“智能储物柜”成品展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1317945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【精选】计算机网络教程(第3章数据链路层)

目录 前言 第3章数据链路层 1、差错检测&#xff08;CRC&#xff09; 2、点对点协议&#xff08;了解应用场景&#xff09; 3、什么是碰撞域&#xff0c;什么是广播域 碰撞域&#xff08;Collision Domain&#xff09;&#xff1a; 广播域&#xff08;Broadcast Domain&a…

linux修改mysql默认端口(很明智的选择因为后面会遇到各种问题)

1. 登录到mysql查看当前的端口号&#xff1a; show global variables like ‘port’; 2.编辑/etc/my.conf 文件添加port3506 这样的样式 保存退出&#xff1b; 内容: mysqld] port3506 datadir/var/lib/mysql socket/var/lib/mysql/mysql.sock usermysql # Disabling sy…

Python-折线图可视化

折线图可视化 1.JSON数据格式2.pyecharts模块介绍3.pyecharts快速入门4.创建折线图 1.JSON数据格式 1.1什么是JSON JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据JSON本质上是一个带有特定格式的字符串 1.2主要功能json就是一种在各个编程语言中流…

【Qt 学习】之摸鱼技巧

1.设置qtcheckbox 默认勾选** 1.1 ui->hexSend->setCheckState(Qt::Checked); 1.2 非UI界面转槽函数 arg1 值 为 0 1 2&#xff0c;三种模式 2.槽函数与信号 使用ui设置按钮槽函数后会在moc_**_cpp文件中有一个对应关系&#xff0c;如果对应关系出错 槽函数和信号就会…

CloudFlare 优选ip 和 优选域名的获取方法

1.CloudFlare优选IP网站:【链接直达】 2.CloudFlare 优选IP工具&#xff1a;【开源软件】 3.CloudFlare 优选域名&#xff1a;【GitHub开源工具】 4.CF优选域名推荐&#xff1a; time.cloudflare.com shopify.com time.is icook.hk icook.tw ip.sb japan.com malaysia.com rus…

qt实现基本文件操作

先通过ui界面实现基本框架 接下来就要实现每个按键的功能了 我们先来实现新建的的功能&#xff0c;我们右键新建键&#xff0c;可以发现没有转到槽的功能&#xff0c;因此我们要自己写connect来建立关系。 private slots:void newActionSlot(); 在.h文件中加上槽函数。 conne…

【Mysql】InnoDB的表空间(九)

概述 表空间是一个在 InnoDB 中比较抽象的概念&#xff0c;对于系统表空间来说&#xff0c;对应着文件系统中一个或多个实际文件&#xff1b;而对于每个独立表空间来说&#xff0c;对应着文件系统中一个名为表名.ibd 的实际文件。可以把表空间想象成由很多个页组成的池子&…

HarmonyOS应用元服务上架

HarmonyOS应用/元服务上架 概述 当您开发、调试完HarmonyOS应用/元服务&#xff0c;就可以前往AppGallery Connect申请上架&#xff0c;华为审核通过后&#xff0c;用户即可在华为应用市场获取您的HarmonyOS应用/元服务。 HarmonyOS会通过数字证书与Profile文件等签名信息来…

CAN 六:CAN过滤器编程举例

1、开发环境 (1)KeilMDK&#xff1a;V5.38.0.0 (2)STM32CubeMX&#xff1a;V6.8.1 (3)MCU&#xff1a;STM32F407ZGT6 2、实现目的 (1)MCU的CAN通过CAN盒连接电脑&#xff0c;接收上位机发送的扩展数据帧。 (2)MCU过滤接收的扩展数据帧&#xff0c;通过串口打印出来作为验…

鸿蒙HarmonyOS开发用什么语言

1.网上流行一句有中国底蕴的话&#xff1a;鸿蒙系统方舟框架盘古大模型。都方舟框架了肯定主推的是ArkUI框架。其实还能使用C、Java和Js开发。 2.从API8开始&#xff0c;Java语言已经从鸿蒙开发剔除了&#xff0c;而官方推荐的是ArkTs.下图是ArkTS与TS、JS的关系。 ArkTs 是TS的…

Nginx location+Nginx rewrite(重写)(新版)

Nginx locationNginx rewrite(重写) Nginx locationNginx rewrite(重写)一、location1、常用的Nginx 正则表达式2、location的类型3、location 的匹配规则4、location 优先级5、location 示例说明5.1只修改网页路径5.2修改nginx配置文件和网页路径5.3一般前缀5.4正则匹配5.5前缀…

【消息中间件】Rabbitmq的基本要素、生产和消费、发布和订阅

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、消息队列的基本要素1.队列:queue2.交换机:exchange3.事件:routing_key4.任务:task 二、生产消费模式1.安装pika2.模拟生产者进程3.模…

git的安装及使用

git的安装及使用 git的安装 官网地址&#xff1a;https://git-scm.com/download/win 在任何位置输入bash或sh,进入git 设置用户名邮箱。 git config --global user.name “wfyfly” git config --global user.email 2423217861qq.com 查看配置信息 git config --list --globa…

【FPGA】Verilog:编码器 | 实现 4 到 2 编码器

0x00 编码器&#xff08;Encoder&#xff09; 编码器与解码器相反。当多台设备向计算机提供输入时&#xff0c;编码器会为每一个输入生成一个与设备相对应的信号&#xff0c;因此有多少比特就有多少输出&#xff0c;以数字形式表示输入的数量。 例如&#xff0c;如果有四个输…

C# 编写Windows服务程序

1.什么是windows服务&#xff1f; Microsoft Windows 服务&#xff08;即&#xff0c;以前的 NT 服务&#xff09;使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动&#xff0c;可以暂停和重新启动而且不显示任何用…

智能优化算法应用:基于天牛须算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于天牛须算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于天牛须算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.天牛须算法4.实验参数设定5.算法结果6.参考文…

[verilog] Verilog 数值表示

主页&#xff1a; 元存储博客 文章目录 前言1. 整数表示1.1 整数数据类型1.2 整数转换函数 2. 负数表示3. 实数表示4. 逻辑电平表示5. 逻辑值表示6. 字符表示法7. 字符串表示 前言 Verilog中&#xff0c;可以使用多种方式表示数值。 1. 整数表示 1.1 整数数据类型 基数格式…

高分通过HarmonyOS基础认证-必学HarmonyOS第一课_附题库答案【官网课程提炼关键知识点-精炼】【鸿蒙专栏-26】

系列文章&#xff1a; HarmonyOS应用开发者基础认证满分答案&#xff08;100分&#xff09; HarmonyOS应用开发者基础认证【闯关习题 满分答案】 HarmonyOS应用开发者高级认证满分答案&#xff08;100分&#xff09; HarmonyOS云开发基础认证满分答案&#xff08;100分&#xf…

博客动态校验+静态校验二次开发方式

静态校验&#xff1a; 1&#xff1a;将需要静态校验的参数继承BaseReqeust类重写validate方法&#xff1a; 动态校验&#xff1a; 1&#xff1a;在需要校验的实现类上加&#xff1a; BizValidate注解 2&#xff1a;写一个校验类&#xff0c;方法命名规范为&#xff1a;需要校…

【013】查看qtawesome 库,并调用库中的元素_#py

qtawesome 库 1. 安装2. 查看库3. 查询图标4. 使用图标 1. 安装 pip install qtawesome -i https://pypi.douban.com/simple-i https://pypi.douban.com/simple 为镜像方法&#xff0c;可以使下载速度变快 2. 查看库 运行下述代码&#xff0c;可以打开下面的窗口。 from qt…