新浪科技

基于MSPM0G3507的非接触门禁控制系统

电子产品世界

关注

1   项目简介

本文引用地址:

在一些特殊的场所,比如传染病病房、手术室等需要严格区分污染区与非污染区的场景,对于非接触来替换一些按键等,就非常有意义。本项目是通过手势传感器来控制隔离门禁的案例。

控制端在正常接收到门禁端的状态信息后,获门禁位置信息,同步显示到OLED 屏上。当手势传感器捕捉到指定动作后,通过CAN 总线发送手势指令。

门禁端在启动后执行自检,将起点到终点的位置检测好,并把运行一次的时间打包通过CAN 总结发送出来。门禁端在接收到指令后,与本身的位置相结合,执行相应的指令。通过PWM 来产生指定频率的脉冲驱动步进电机,通过DIR 高低电平设置来改变电机运行方向,通过滑台来实现门禁的打开与关闭功能。

实现功能,手势向上,关闭门禁,手势向下,打开门禁,手势下压,急停。

2   硬件结构图

1.1 Bom表(写明器件型号);

【1】

【2】

【3】

【4】

【5】

【6】

1.2 软件开发:

【开发平台】

Code Composer Studio Version: 12.7.0.00007

【外设的配置】

本项目主要的外设由IIC、PWM、CAN 来驱动。

1.1.1 IIC 的配置

配置为1M 的速率,同时开启收接中断,配置发送与接收的缓冲区。

1.1.2 PWM的配置

选择pwm0,以及通道0 为输出,配置输出1KHz的输出波形,占空比50%。

配置输出的IO为PB0

1.1.3 CAN的配置

配置仲裁速率为250K,数据传输速率为2M。

选择RX为PA13,TX为PA12

保存后生成工程。

【公共代码】

CAN 发送与接收代码

1. CAN 接收中断函数的实现:

void MCAN0_INST_IRQHandler(void)

{

switch ( DL_MCAN_getPendingInterrupt(MCAN0_INST)) {case DL_MCAN_IIDX_LINE1:

/ * Check MCAN interrupts fired during TX/RX of CAN package */gInterruptLine1Status |= DL_MCAN_getIntrStatus(MCAN0_INST);

DL_MCAN_clearIntrStatus(MCAN0_INST,

gInterruptLine1Status,

DL_MCAN_INTR_SRC_MCAN_LINE_1);

gServiceInt = true;

break;

default:

break;

}

代码中如果有CAN 的数据接收,则更新接收标志

gServiceInt。

在主循环中判断接收接收标志位,如果为真则调用数据处理函数

if(true == gServiceInt)

{

gServiceInt = false;

rxFS.fillLvl = 0;

if ((gInterruptLine1Status & MCAN_IR_RF0N_MASK) == MCAN_IR_RF0N_MASK) {rxFS.num = DL_MCAN_RX_FIFO_NUM_0;

while ((rxFS.fillLvl) == 0) {

DL_MCAN_getRxFIFOStatus(MCAN0_INST, &rxFS);

}

DL_MCAN_readMsgRam(MCAN0_INST, DL_MCAN_MEM_TYPE_FIFO, 0U, rxFS.num, &rxMsg);

DL_MCAN_writeRxFIFOAck(MCAN0_INST, rxFS.num, rxFS.getIdx);

processRxMsg(&rxMsg);

gInterruptLine1Status &= ~(MCAN_IR_RF0N_MASK);

/* Add request for transmission. */

}

在数据处理函数中,如果是门禁端,则判断是否为命令的ID,如果是则根据命令设置电机运行方向:

void processRxMsg(DL_MCAN_RxBufElement

*rxMsg)

{

uint32_t idMode;

uint32_t id;

idMode = rxMsg->xtd;

if (ID_MODE_EXTENDED == idMode) {id = rxMsg->id;

} else {

/ * Assuming package is using 11-bit standardID.

* When package uses standard id, ID is stored inID[28:18]*/

id = ((rxMsg->id & (uint32_t) 0x1FFC0000) >>(uint32_t) 18);

}

switch (id) {

case 0x3:

mydoor_t.run_dir = rxMsg->data[1];

set_dir();

break;

default:

/* Don’t do anything */break;

}

在控制端则对接收的ID 进行判断,并进行数据解析:

void processRxMsg(DL_MCAN_RxBufElement*rxMsg)

{

uint32_t idMode;

uint32_t id;

idMode = rxMsg->xtd;

if (ID_MODE_EXTENDED == idMode) {id = rxMsg->id;

} else {

/ * Assuming package is using 11-bit standardID.

* When package uses standard id, ID is stored inID[28:18]*/

id = ((rxMsg->id & (uint32_t) 0x1FFC0000) >>(uint32_t) 18);

}

switch (id) {case 0x4:

mydoor_recv.run_state = rxMsg->data[0] ;

mydoor_recv.run_dir = rxMsg->data[1] ;

mydoor_recv.start_flage = rxMsg->data[2] ;

mydoor_recv.stop_flage = rxMsg->data[3] ;

mydoor_recv.run_one_time = rxMsg->data[4]+ (rxMsg->data[5]<<8);

mydoor_recv.run_this_time = rxMsg->data[6]

+ (rxMsg->data[7]<<8);

mydoor_recv.sta_link = 1 ;

break;

default:

/* Don’t do anything */

break;

}

CAN 发送:

在进入主循环中,先对CAN 的数据进行初始化:

txMsg0.id = ((uint32_t)(0x4)) << 18U;

/* Transmit data frame. */

txMsg0.rtr = 0U;

/* 11-bit standard identifier. */

txMsg0.xtd = 0U;

/* ESI bit in CAN FD format depends only on

error passive flag. */

txMsg0.esi = 0U;

/* Transmitting 4 bytes. */

txMsg0.dlc = 8U;

/ * CAN FD frames transmitted with bit rate

switching. */

txMsg0.brs = 1U;

/* Frame transmitted in CAN FD format. */

txMsg0.fdf = 1U;

/* Store Tx events. */

txMsg0.efc = 1U;

/* Message Marker. */

txMsg0.mm = 0xAAU;

/* Data bytes. */

txMsg0.data[0] = LED0_STATUS_ON;

txMsg0.data[1] = 0x00;

并根据对应的功能进行数据封门,并执行数据发送功能:

if(1==mydoor_t.start_flage)

{

DL_TimerG_stopCounter(PWM_0_

INST);

mydoor_t.run_one_time = Tick -

mydoor_t.run_this_time;

mydoor_t.run_this_time = 0;

door_flag = 2;

// 记录总时间

txMsg0.data[4] = (uint8_t)mydoor_

t.run_one_time;

txMsg0.data[5] = (uint8_t)(mydoor_

t.run_one_time>>8);

// 记录起始时间

/ * Write Tx Message to the Message

RAM. */

DL_MCAN_writeMsgRam(MCAN0_

INST, DL_MCAN_MEM_TYPE_BUF, 0, &txMsg0);

/* Add request for transmission. */

DL_MCAN_TXBufAddReq(MCAN0_

INST, 0);

}

【PWM】

在pwm 代码方面只需要启动或者关闭定时器就行了

void run_motor(void)

{

switch (mydoor_t.run_dir)

{

case 0x01:

if(mydoor_t.stop_flage == 0) // 如果没有达到终点

{

DL_TimerG_startCounter(PWM_0_INST);

}

else{

DL_TimerG_stopCounter(PWM_0_INST);

}

break;

case 0x02:

if(mydoor_t.start_flage == 0)

{

DL_TimerG_startCounter(PWM_0_INST);

}

else

{

DL_TimerG_stopCounter(PWM_0_INST);

mydoor_t.run_state = 0;

}

break;

default:

DL_TimerG_stopCounter(PWM_0_INST);

mydoor_t.run_state = 0;

break;

}

【IIC 驱动】

在工程中添加i2c 的驱动封装i2c_app.c/h

这个驱动封装了读写两个驱动,可实现与硬件低层的解耦。具体代码见附件。

【OLED】

使用公有的OLED 驱动库,只需要封装OLED_WR_Byte 即可实现驱动的移植。

void OLED_WR_Byte(uint8_t dat, uint8_t mode)

{

if (mode)

{

i2c_app_write(I2C_OLED_INST, 0x3C, data,&dat, 1);

}

else

{

i2c_app_write(I2C_OLED_INST, 0x3C, cmd,&dat, 1);

}

手势传感器有现成的驱动库,我这里只需要添加iic的读写驱动即可。

【PAJ7620 手势传感器】

PAJ7620 移植驱动,与OLED 一样也只需要重写读写函数即可以完成驱动的移植

static void DEV_I2C_WriteByte(I2C_Regs *PAJ7620U2_I2C,uint8_t add_, uint8_t data_)

{

uint8_t Buf[1] = {0};

Buf[0] = data_;

i2c_app_write(I2C1, 0x73, add_,Buf, 2);

}

static void DEV_I2C_WriteWord(I2C_Regs *PAJ7620U2_I2C,uint8_t add_, uint16_t data_)

{

uint8_t Buf[2] = {0};

Buf[0] = data_ >> 8;

Buf[1] = data_;

i2c_app_write(I2C1, 0x73, add_,Buf, 2);

}

static uint8_t DEV_I2C_ReadByte(I2C_Regs *

PAJ7620U2_I2C,uint8_t add_)

{

uint8_t state;

uint8_t Buf[1];

Buf[0] = add_;

state = i2c_app_read(I2C1, 0x73, add_,Buf, 1);

if(state == 0)

{

return Buf[0];

}

else

return state;

}

以上是主要代码的介绍。

【程序流程图】

门禁端

3   项目总结

本项目主要是在利用Ti 的MSPM0G3507 这颗优秀的MCU 来实现特殊环境下的无接触的门禁控制,可以实现多点对一点控制,一对多的数据交互。

整个项目的亮点就是MSPM0G3507 拥有80M 主频, 搭载了FDCAN 高速总线, 可以轻松实现多个MCU 的组网,相比传感的485 的总线组网有质的提升,FDCAN 总线可以实现多对多的组网。同时这个MCU还有高速的IIC 总线,可以实现一路IIC 驱动多个如OLED、PAJ7620 的外设。

同时使用了MSPM0G3507 的PWM 外设,可以精准的驱动步进电机。实现如门禁等电机控制场景。

加载中...