1 项目简介
本文引用地址:
在一些特殊的场所,比如传染病病房、手术室等需要严格区分污染区与非污染区的场景,对于非接触来替换一些按键等,就非常有意义。本项目是通过手势传感器来控制隔离门禁的案例。
控制端在正常接收到门禁端的状态信息后,获门禁位置信息,同步显示到OLED 屏上。当手势传感器捕捉到指定动作后,通过CAN 总线发送手势指令。
门禁端在启动后执行自检,将起点到终点的位置检测好,并把运行一次的时间打包通过CAN 总结发送出来。门禁端在接收到指令后,与本身的位置相结合,执行相应的指令。通过PWM 来产生指定频率的脉冲驱动步进电机,通过DIR 高低电平设置来改变电机运行方向,通过滑台来实现门禁的打开与关闭功能。
实现功能,手势向上,关闭门禁,手势向下,打开门禁,手势下压,急停。
2 硬件结构图

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

【1】

【2】

【3】

【4】

【5】

【6】
【7】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 外设,可以精准的驱动步进电机。实现如门禁等电机控制场景。
“掌”握科技鲜闻 (微信搜索techsina或扫描左侧二维码关注)










