简单说明
在代码实现部分会给出设计理念和分析,整体资源可以直接下载压缩包(手机端依然看不到,还是不知道为什么)。
使用设备
按照题目要求需要制作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
成品展示
“智能储物柜”成品展示