前言

网站

广东省职业院校学生专业技能大赛官网

[平台(]https://dev.bj-jc.com:20002/RemoteExp/#/stu-exp-panel)

参考文章/代码

在线工具

官方资料

  • 2021-2022 智能硬件应用开发赛项竞赛规程
  • 智能硬件应用开发平台(使用说明书)型号:JC-IHAP-V1.0
  • 智能硬件应用开发平台(安装说明书)型号:JC-IHAP-V1.0
  • 智能硬件应用开发平台(AGV小车安装说明书)型号:JC-IHAP-V1.0
  • 2020-2021年度广东省职业院校技能大赛高职组智能硬件应用开发赛项样题
  • 智能硬件应用平台高职-系统方案介绍
  • 沙盘启动说明文档
  • 垃圾分类沙盘总体说明
  • 垃圾桶控制说明

所用手册资料+思维导图XMind 阿里云网盘:https://www.aliyundrive.com/s/Xj1tqTZn9je

图片

智能硬件小车总体流程图

需要安装的软件

  • Keil uVision5 MDK + stm32F4芯片包
  • Altium Designer 20(看原理图用)
  • 待补充

小车信息(JC-SC)

组成 具体参数
底盘 ① 尺寸 400×307×123mm;
② 颜色:蓝;
③ 负载能力:10kg;
④ 底盘高度:21.5mm
麦克纳姆轮 ① 直径:100mm; ② 轴向宽度:50mm;
③ 板数:2; ④ 滚轮数:9;
⑤ 材质:铝合金;⑥ 净重:0.4kg;⑦ 负载能力:15kg。
联轴器 ① 规格:6mm 内径;
② 材质:铝合金。
电机 ① 工作电压:12v;② 额定功率:17w;
③ 空载转速:8100rpm;④ 减速比:64:1;
⑤ 输出轴:6mm D 型
编码器 ① 类型:A/B 相增量式磁编码器;
② 线数:12;
③ 供电电压:5V。
机械臂/机械手 ① 机械臂:三自由度高精度步进驱动机械臂,前伸距离:442mm,缩回距离:254mm,最高 点: 322mm,最低点:-159mm;
② 配备高精密 42 行星减速步进电机,减速比:1:10;
③ 电机驱动电压:12V;④ 工作最大电流:4.5A;
⑤ 机械手最大尺寸:53mm,5V 舵机驱动
电机驱动 ① 控制板:额定输入电压:DC 12V/24V;② 输出通道数:4 路;③ 每路额定输出电流:7A;④ 额定输出功率:84W(12V 供电);⑤ 控制信号电压:3~6.5V;⑥ PWM 频率范围:0~10kHz
超声波模块 ① 工作电压:DC5V;② 工作电流:15mA;
③ 工作频率:40kHz;④ 最远射程:4m;最近射程:2cm;
⑤ 测量角度:15 度。
磁导航传感模块 ① 检测极性:SorN;
② 输入电压:9~28V;
③ 输出方式:NPN-OC&RS232。
串口无线通信模块 芯片 SI4438,工作频率:425~525MHZ;
锂 离 子聚 合物 电 池 组 电 池 组 :12.8V 8000mAh;

机械臂组成 作用 采用芯片
底盘 用于机械臂的左右转动,在底盘的前方有一个位置传感器,用于底盘初始位置的校准定位 采用42步进电机加减速装置运动的,控制方式是给一个脉冲转动一定的角度,所以只要不丢步,运行的位置是确定的(前提是上电后执行位置初始化)
大臂 用于前后转动,在大臂上有一个位置传感器,用于大臂的初始位置的定位 同上
小臂 用于上下转动,在小臂上有一个位置传感器,用于小臂的初始位置的定位 同上
舵机夹子 夹取物品 使用舵机控制的,舵机采用PWM占空比驱动,PWM占空比决定了夹子的位置,所以不需要上电校准,只要上电后保持最大张开角就可以
激光测距板 能够探测前面物体的距离,用于检测夹子前面是否有物块,左右移动机械臂来找准物块 /

STM32口袋机信息(JC-SPZII)

组成 具体参数
MCU STM32F407ZG
LED 8 个单色 LED 灯,5 个LED 电源指示灯
按键 8 个方向圆盘电容式按键,具有震动 体感反馈;1 个电子物理按键,具有复位和开 机功能
拨码开关 5 位拨码开关,控制 BOOT、体感 震动等
LCD 液晶屏 65535 真彩,2.4 寸,分辨率: 240*320,4 线 SPI 接口
高速模拟输入 /
SRAM 静态内存 512K*16bit/1MB
以太网口 /
DAC 模拟输出 /
ADC 模拟输入 1 路 12bit-ADC 模拟信号采 集
TF 卡 标准的 SD 卡协议
TYPE-C 板载 TYPE-C 接口,具有烧写、调 试、仿真等功能
USB USB 主从接口
供电电压 7~24V、1A 电源适配器;5V、 3A 的 TYPE-C
GPIO 接口 可用 GPIO 共60pin
音频 集有高质量的立体双声道

智能分拣线信息(JC-IGSP)

组成 具体参数
环形输送线 ① 柔性链板输送线参数;长 1500 宽 450 高
② 250 12V 直流电机驱动。
③ 柔性链板输送线组成;由主动轮、被动轮、铝合金支架、铝合金导轨、衬条及护栏组成。
图像采集 ① 工业相机:CMOS 光学传感器尺寸 1/2.5、分辨率 2592X1944、帧率 8FPS; 柔性链板输送线参数;长 1500 宽 450 高
② 工业镜头焦距 8mm;
③ LED 环形白光光源;
④ 专业可调光源控制器;
⑤ 可调式相机和光源金属支架
推杆装置 最大尺寸:53mm,5V 舵机驱动(MG995型号)
处理器 ① GPU:128 核心 Maxwell
② CPU:四核 ARM A57@1.43GHZ

集中站信息(JC-ICS)

组成 具体参数
整体结构 整体架构采用铝合金框架式模块化结构,面板采用理化板。
集中回收区 ① LED 显示屏参数:供电电压 5V、LED 点阵屏,可以显示 16 个汉字(16 x 16 分辨率)
② 仓库尺寸:1550 x 300 x 450(mm)
③ 含四个门、能够自动开启
④ 每个仓库门采用模拟舵机控制
⑤ 地图尺寸: 2000mm x 3000mm,采用 PVC 地板加工业打印技术,构造回收系统展示情景。
⑥ 台面贴有 10mm 宽黑色磁引导带,采用 N 极向上方式,以便小车巡线行驶
⑦ 4 路舵机控制接口
⑧ 一个 LED 点阵屏(分辨率 256 x 16)驱动接口(8080 接口)
⑨ 4 路 SW2812 彩灯控制接口,可以控制每个集中站垃圾箱灯的颜色和亮度
⑩ 板载 433MHz 的无线通讯模块,可以与其沙盘上的其他设备无线通讯

智能回收垃圾桶信息(JC-SRD)

组成 具体参数
整体 ① 尺寸:240 x 310 x 402mm;
② 重量:3.5Kg;
③ 材料:高强度冲击 ABS;
④ 红 外 感 应 器 : 高 灵 敏 度 , 延 时 时 间0.5-200S,可调
⑤ 涡轮式真空压缩机:高速涡轮式真空压缩机,转数:12v5000
⑥ 高温自动封袋器:(横向纵向双向熔接,功率 10W)
⑦ 称重传感器:精度 1 克
⑧ 内置铅酸电池:12V 2.2AH
⑨ 适配器规格:13.8V 0.65A
⑩ 4 路直流电机 H 桥控制输出:12V 输出,最大电流 1A,分别控制小盖的开合,大盖的开合,自动封口机械 X 轴和 Y 轴的行程,限位开关接口
⑪ 1 路使用 PWM 控制的电热丝加热输出接口,用于垃圾袋的封口和熔断
⑫ 1 路红外反射传感器接口,用于手势识别
⑬ 1 路红外对射传感器接口,用于检测垃圾桶中是否有垃圾袋
⑭ 1 路称重传感器接口,最大量程 5kg
⑮ 板载 433MHz 的无线通讯模块,可以与其沙盘上的其他设备无线通讯

比赛须知(来自官方手册)

  • 比赛提供 STM32 初始化及各类传感、控制、驱动和通信组件 的基本驱动程序,提供标准函数调用接口和详细说明。提供各网络终端数据交互所需 MQTT 协议代码及接口调用说明
  • 根据参赛队竞赛成绩排名分别设立一、二、三等奖。以各赛项实际参赛队数量为基数,一、 二、三等奖获奖比例分别为 15%、25%、40%(小数点后四舍五入)

比赛步骤

比赛步骤

裁判将写有数据的射频卡给选手,选手刷卡,口袋机经过读卡器读取射频卡中的任务参数。

比如:识别几个垃圾,全部垃圾全部扔到垃圾桶后,将全部垃圾打包然后扔到回收站中。

运行步骤

1.将垃圾放在传送带上,然后刷卡,传送带开始转动,同时摄像头开始识别;识别后的垃圾按照分类分别推送到托盘上

2.小车开始抓取托盘上的垃圾,然后扔到指定的垃圾桶中

3.将全部垃圾扔到垃圾桶后,全部垃圾桶开始打包垃圾

4.小车将打包好的垃圾扔到指定的集中站中

5.全部任务完成

STM32F407ZG介绍

口袋机部分

口袋机调试

用仿真器下载程序调试,USB线插 TYPE-C 口(需要下载驱动 dpinst_amd64),串口调试助手需要先安装驱动(CP210xVCPInstaller_x64),如果串口接收没反应可把USB重新插一下

口袋机基础模块学习

延时函数

  • SysTick_Config函数的参数是systick重装定时器的值, RCC_Clocks.HCLK_Frequency 可以通过调试知道是 168000000,168000000/1000 = 168000;168000/168000000 = 0.001s=1ms
/*************************delay.h*************************/
# include "stm32f4xx.h"


extern __IO uint32_t LocalTime;

void TimingDelay_Decrement(void);

void delay_Init(void);
void delay_ms(__IO uint32_t nTime);
/*************************delay.c*************************/
static __IO uint32_t TimingDelay;
__IO uint32_t LocalTime = 0;

void delay_Init(void)
{
	 RCC_ClocksTypeDef RCC_Clocks;
	
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);  //配置SysTick定时器时钟源为HCLK
  RCC_GetClocksFreq(&RCC_Clocks);
	
	
	if (SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000))//SysTick定时器每1ms中断一次
  {
    /* Capture error */
    while (1);
  }
  NVIC_SetPriority (SysTick_IRQn, 0);             //配置SysTick定时器的优先级
}

void delay_ms(__IO uint32_t nTime)
{
  TimingDelay = nTime;

  while(TimingDelay != 0);
}

void TimingDelay_Decrement(void)
{
  if (TimingDelay != 0x00)
  {
    TimingDelay--;
  }
	
	LocalTime++;
}

查看时钟源

在main函数添加下面两行代码:

RCC_ClocksTypeDef     RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);

用ST-Link连接,打开keil调试即可看到

点阵

所用模块 连接注意
STM32模块扩展板 插口袋机右边
点阵屏模块 背面印有P1的那边插GND,不能插反否则会烧坏模块

硬件连接:

  • 点阵模块 行驱动是两片74HC138, 列驱动是两片74Hc595芯片

  • 74HC595芯片需要懂:

是一个 移位寄存器,它是 8位串行输入,并排输出

8位串行输入:所谓8位串行输入,就是由8个数字组成,每一个数字占据一个位,共有8个位,像串在一起一样以一行的方式输入到蓝色方框内(蓝色框就是我们的芯片)

移位寄存:它不是直接进去的,而是以 “移位” 的方式进去的,每次只移动一个数字进去,蓝色方框内最多可以移进去8个数字,也就说 74HC595移位寄存器 最多可存储8个数字,称为 “寄存”

绿色方框 = SH_CP(CLK),移位寄存器的输入条件就是SH_CP这个引脚,当它处于 高电平 时,数字才能被送进74HC595;

蓝色圆球 = ST_CP(SCK),当 “并排输出开关” 被触发时,74HC595移位寄存器里面存储的数字就会被输出出来,它不是一个一个的出来的,而是并排同时出来,称为 “并排输出”

  • 74HC138芯片要懂

程序

由图可知P1是输入,P2是输出,故只需编程P1管脚即可,点阵是高电平亮

P1管脚号 对应扩展板管脚
GND GND
D PA4
C PD7
B PA5
A PF1
悬空(可不管) PD12
DINT PD14
SCK PD15
CLK PF14
3.3V 3.3V
  • 由原理图可知 D 是用来区分哪一块138芯片的,当 D 低电平时连接 u1;当D 高电平时连接 u4
  • 一个8*8点阵从左到右是 低位-->高位(对应1248码就是 1–2–4–8–16–32–64–128)
  • 取模软件生成四个角点亮步骤:选择16*16 — 描点(黑色表示 1,高电平) —图像左右调换 — 参数设置( 横向取模,任何时候都加0) — C51格式 — 复制粘贴即可
/*************************STM32F40x_ GPIO Init.h*************************/

# define CLK_L		(GPIOF->BSRRH = GPIO_Pin_14)	//控制PF14输出低电平
# define CLK_H		(GPIOF->BSRRL = GPIO_Pin_14)	//控制PF14输出高电平

# define LAT_L		(GPIOD->BSRRH = GPIO_Pin_15)	//SCLK
# define LAT_H		(GPIOD->BSRRL = GPIO_Pin_15)	//SCLK

# define DIN_L		(GPIOD->BSRRH = GPIO_Pin_14)
# define DIN_H		(GPIOD->BSRRL = GPIO_Pin_14)


# define A_L		(GPIOF->BSRRH = GPIO_Pin_1)
# define A_H		(GPIOF->BSRRL = GPIO_Pin_1)

# define B_L		(GPIOA->BSRRH = GPIO_Pin_5)
# define B_H		(GPIOA->BSRRL = GPIO_Pin_5)

# define C_L		(GPIOD->BSRRH = GPIO_Pin_7)
# define C_H		(GPIOD->BSRRL = GPIO_Pin_7)

# define D_L		(GPIOA->BSRRH = GPIO_Pin_4)
# define D_H		(GPIOA->BSRRL = GPIO_Pin_4)

/*************************STM32F40x_ GPIO Init.c*************************/
//端口初始化
void GPIO_init_all(void)
{
	GPIO_init(GPIO_F,GPIO_Pin_14,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//CLK
	GPIO_init(GPIO_D,GPIO_Pin_15,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//SCK
	GPIO_init(GPIO_D,GPIO_Pin_14,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//DIN
	GPIO_init(GPIO_D,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//悬空
	GPIO_init(GPIO_F,GPIO_Pin_1,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//A
	GPIO_init(GPIO_A,GPIO_Pin_5,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//B
	GPIO_init(GPIO_D,GPIO_Pin_7,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//C
	GPIO_init(GPIO_A,GPIO_Pin_4,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//D
}

/*************************main.c*************************/

u8 dis_buf_test[32] = 
{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};	//测试数据,全亮
u32 dis_count = 0;

void dis_flash_fun(u8 *p);
void decode_138_fun(u8 row);
void hc595_out(unsigned char data);

int main(void)
{
    //...
    GPIO_init_all();//初始化所有要使用的端口
    for(dis_count=0;dis_count<1;dis_count++)//循环显示1次测试数据,测试数据是把全部LED点亮,用于测试模块,看有无坏点
	{
		dis_flash_fun(dis_buf_test);
	}
    while(1)
    {
        
    }
}

//扫描16*16 点阵  输入参数是 一个32字节的数组  数据依次是第一行的右边  第一行的左边 
void dis_flash_fun(u8 *p)
{
	u8 i;
	u8 count;
	
	count = 0;
	
	for(i=0;i<16;i++)
	{
		decode_138_fun(i);//行扫描
		
		hc595_out(*(p+count));//串行输出第一个字节,给右边的8*8 LED
		count++;
		hc595_out(*(p+count));//串行输出第二个字节,给左边的8*8 LED
		count++;
		
		delay_ms(1);//延时1ms  使得图像停留1ms的时间  不然会扫描的太快
	}
}
//控制两片138译码器  输入0-15  控制端口ABCD 依次高电平,实现行扫描
void decode_138_fun(u8 row)
{
	switch(row)
	{
		case 0:A_L;B_L;C_L;D_L;break;	//000 0
		case 1:A_H;B_L;C_L;D_L;break;	//100 0
		case 2:A_L;B_H;C_L;D_L;break;	//010 0
		case 3:A_H;B_H;C_L;D_L;break;	//110 0
		case 4:A_L;B_L;C_H;D_L;break;	//001 0
		case 5:A_H;B_L;C_H;D_L;break;	//101 0
		case 6:A_L;B_H;C_H;D_L;break;	//011 0
		case 7:A_H;B_H;C_H;D_L;break;	//111 0
		case 8:A_L;B_L;C_L;D_H;break;	//000 1
		case 9:A_H;B_L;C_L;D_H;break;	//100 1
		case 10:A_L;B_H;C_L;D_H;break;	//010 1
		case 11:A_H;B_H;C_L;D_H;break;	//110 1
		case 12:A_L;B_L;C_H;D_H;break;	//001 1
		case 13:A_H;B_L;C_H;D_H;break;	//101 1
		case 14:A_L;B_H;C_H;D_H;break;	//011 1
		case 15:A_H;B_H;C_H;D_H;break;	//111 1
		
	}
}
//74hc595时序函数    data是要并行输出的8位数据
void hc595_out(unsigned char data)
{
	unsigned char i,temp;

	for(i=0;i<8;i++)
	{
		if((temp&0x80)==0x80)
		{
			DIN_H;
		}
		else
		{
			DIN_L;
		}
		temp = temp<<1;
		CLK_H;
		CLK_L;
		LAT_H;
		LAT_L;
	}
}

WIFI

所用模块 连接注意
STM32模块扩展板 插口袋机左边
WIFI模块 模块有按键的朝右边

硬件连接:

管脚号 对应扩展板管脚
RST PG13
RX PD2(USART5)
TX PC12(USART5)

wifi模块的使用步骤是:(都是使用AT命令实现的)
① 关闭回显
② 设置模式
③ 连接路由器
④ 连接服务器
⑤ 发送数据

  • 首先在手机开热点,热点名和密码记住,在代码里填写(注意数组大小也要改)
  • 打开网络适配器查看IP,代码里电脑端那填 IPv4地址

  • 注意命令里""内容是用反斜杠 \ 隔着的(数组大小不包括它)
  • 单片机给wifi模组发送AT指令后, 需要稍微延时 等待wifi模组应答,读的太快会读不到wifi应答的AT指令,会达不到研发要求
  • 回显的含义是:发送什么指令,返回给你结果的时候,会把发送的指令再显示一遍
  • LCD显示的自己定义的数组时需要 len-2(把/r/n去掉),显示Wifi返回的数据时不需要
程序

ESP8266.c

/*************************ESP8266.c*************************/
# define	CW_LEN	35		// CWJAP 数组的字符串长度(不包括\)

char CWJAP[CW_LEN] = {"AT+CWJAP=\"yang5201314\",\"00000000\"\r\n"};//yang5201314是WIFI的ssid  00000000是密码(手机热点)

# define	CIPSTART_LEN	39   // CIPSTART数组的字符串长度(不包括\)
char CIPSTART[CIPSTART_LEN] = {"AT+CIPSTART=\"TCP\",\"172.20.10.7\",12345\r\n"};//172.20.10.7是电脑端的IP地址  12345是端口号

char CWMODE[13] = {"AT+CWMODE=1\r\n"};//设置模式  不用改变

char CIPMUX[13] = {"AT+CIPMUX=0\r\n"};// 设置模式  不用改变

char CIPSEND_5[14] = {"AT+CIPSEND=5\r\n"};//连接上服务器后  需要发送数据的命令  5是要发送数据的长度

char CIPSEND_9[14] = {"AT+CIPSEND=9\r\n"};// 连接上服务器后  需要发送数据的命令  5是要发送数据的长度

char send_buf[5] = {"12345"};//要发送的数据

char GOT_IP[6] = {"GOT IP"};//定义一个数组

char OK_buf[2] = {"OK"};//定义一个数组

char http_race_buf[20];//收到服务器返回的数据

char get_time_cmd_buf[9] = {"get time;"};//向服务器发送 获取时间命令


extern char usart5_race_buf[100];//串口5 接收数组
extern u16 usart5_race_count;		//串口5接收数据计数


//关闭回显
u8 ATE_fun(void)
{
	u8 temp;
	
    //串口5 向WIFI模块 发送ATE0 命令
	Uart5_send_data("ATE0\r\n",6);													//串口1 给电脑也发送
	Usart1_send_data("ATE0\r\n",6);													//口袋机屏幕显示
	LCD_ShowString_stm32_send("ATE0\r\n",4);	
    //延时
	delay_ms(500);
    //串口1 发送 串口5收到的数据(wifi模块返回的数据)
	Usart1_send_data(usart5_race_buf,usart5_race_count);	
    //屏幕显示 wifi模块返回的数据
	LCD_ShowString_stm32_race(usart5_race_buf);							
	//串口5再发送一次ATE0  有时候第一次发送回有问题
	Uart5_send_data("ATE0\r\n",6);													//串口1给电脑发送ATE0
	Usart1_send_data("ATE0\r\n",6);													//延时
	delay_ms(500);	
    //串口1发送  wifi模块返回的数据
	Usart1_send_data(usart5_race_buf,usart5_race_count);
    //屏幕显示
	LCD_ShowString_stm32_race(usart5_race_buf);							
	//检查usart5_race_buf数组中  是否有OK 字符
	temp = chack_have_fun(OK_buf,2,usart5_race_buf,30);			
	if(temp==1)//有 说明ATE0 设置成功
	{
		printf("ATE-ok\r\n");
		return 1;
	}
	else
	{
		printf("ATE-error\r\n");
		return 0;
	}
	
}
//连接WIFI
u8 CWJAP_fun(void)
{
	u8 temp;
    
	//发送连接路由器命令   CWJAP 是要发送的数组
	Uart5_send_data(CWJAP,CW_LEN);
    //同时也给电脑串口发送
	Usart1_send_data(CWJAP,CW_LEN);	
    //屏幕显示
	LCD_ShowString_stm32_send(CWJAP,CW_LEN-2);	
    //延时10秒   有时候时间比较长
	delay_ms(20000);														
	//串口1 向电脑发送  wifi模块返回的数据
	Usart1_send_data(usart5_race_buf,usart5_race_count);
    //屏幕显示wifi模块返回的数据
	LCD_ShowString_stm32_race(usart5_race_buf);						
	//判断 usart5_race_buf 收到的数据中 是否有GOT_IP 字符串
	temp = chack_have_fun(GOT_IP,6,usart5_race_buf,30);		
	
	if(temp==1)//连接路由器成功
	{
		printf("WIFI_link_ok\r\n");
		return 1;
	}
	else
	{
		printf("WIFI_link_error\r\n");
		return 0;
	}
}
//设置模式  wifi 模块初始化
u8 CWMODE_fun(void)
{
	u8 temp;
	
	Uart5_send_data(CWMODE,13);
	Usart1_send_data(CWMODE,13);
	LCD_ShowString_stm32_send(CWMODE,11);
	delay_ms(1000);
	
	Usart1_send_data(usart5_race_buf,usart5_race_count);
	LCD_ShowString_stm32_race(usart5_race_buf);
	
	temp = chack_have_fun(OK_buf,2,usart5_race_buf,30);
	if(temp==1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
	
}


//设置模式  wifi模块初始化  一般不用修改
u8 CIPMUX_fun(void)
{
	u8 temp;
	
	Uart5_send_data(CIPMUX,13);
	Usart1_send_data(CIPMUX,13);
	LCD_ShowString_stm32_send(CIPMUX,11);
	delay_ms(1000);
	
	Usart1_send_data(usart5_race_buf,usart5_race_count);
	LCD_ShowString_stm32_race(usart5_race_buf);
	
	temp = chack_have_fun(OK_buf,2,usart5_race_buf,30);
	if(temp==1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//发送域名和端口  连接服务器
u8 CIPSTART_fun(void)
{
	u8 temp;
	
    // 连接的域名和端口在CIPSTART数组中  上面有定义
	Uart5_send_data(CIPSTART,CIPSTART_LEN);
	Usart1_send_data(CIPSTART,CIPSTART_LEN);
    //屏幕显示
	LCD_ShowString_stm32_send(CIPSTART,CIPSTART_LEN-2);	
	delay_ms(5000);
	//串口1 发送 wifi模块返回的数据
	Usart1_send_data(usart5_race_buf,usart5_race_count);
    //屏幕显示
	LCD_ShowString_stm32_race(usart5_race_buf);					
	//检查usart5_race_buf wifi模块返回的数据中  是否有 OK 字符串
	temp = chack_have_fun(OK_buf,2,usart5_race_buf,30);	
	if(temp==1)//连接成功
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//向服务器发送数据
u8 CIPSEND_fun(void)
{
	u8 temp;
    
	//发送CIPSEND_5 命令  定义在最上面
	Uart5_send_data(CIPSEND_5,14);		
    //串口1也发送  用于串口监测
	Usart1_send_data(CIPSEND_5,14);		
    //屏幕显示
	LCD_ShowString_stm32_send(CIPSEND_5,12);
    //延时5秒
	delay_ms(5000);																	//串口1 显示 wifi模块返回的数据
	Usart1_send_data(usart5_race_buf,usart5_race_count);
    //屏幕显示wifi 返回的数据
	LCD_ShowString_stm32_race(usart5_race_buf);						
	//监测usart5_race_buf 数组(wifi模块返回的数据)中 是否有OK 字符串
	temp = chack_have_fun(OK_buf,2,usart5_race_buf,30);		
	//向服务器发送数据  send_buf就是要发送的数组  发送5个字节  send_buf在上面有定义
	Uart5_send_data(send_buf,5);													//延时1秒
	delay_ms(1000);																	//串口打印 返回的数据
	Usart1_send_data(usart5_race_buf,usart5_race_count);
    //屏幕显示返回的数据
	LCD_ShowString_stm32_race(usart5_race_buf);						
	//监测usart5_race_buf数组 wifi模块返回的数据中有无 OK 字符串
	temp = chack_have_fun(OK_buf,2,usart5_race_buf,30);		
	if(temp==1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//检查数组中有无字符串  没有返回0  有就返回位置数据
u8 chack_have_fun(char *flr, u8 len, char *buf, u16 mun)
{
	u8 i;
	u16 j;
	
	u8 temp;

	for(j=0;j<mun-len+1;j++)
	{
		temp = 0;
		for(i=0;i<len;i++)
		{
			if(*flr==*buf)
			{
				temp++;
			}
			flr++;
			buf++;
		}
		flr = flr - len;
		buf = buf - len;
		buf++;
		if(temp==len)
		{
			return 1;
		}
	}
	return 0;
}
/*************************main.c*************************/
int main(void)
{
    //...
	RST_L;	//Wifi复位键拉低
	delay_ms(10);
	RST_H;	//Wifi复位键拉高
    delay_ms(3000);	//延时3s
    
    Uart5_Init();//串口5  与wifi模块连接
    ATE_fun();//关闭串口回显
    CWJAP_fun();//连接WIFI
	CWMODE_fun();//设置模式
	CIPMUX_fun();//设置模式
	CIPSTART_fun();//连接服务器
    
    while(1)
    {
        CIPSEND_fun();//发送数据给服务器
		delay_ms(5000);
    }
    
}

语音识别

所用模块 连接注意
STM32模块扩展板 插口袋机右边
语音识别模块 模块话筒在左上角边

硬件连接:

管脚号 对应扩展板管脚
CLK(SPI时钟) PA4
MOSI(SPI数据输入) PD7
MISO(SPI数据输出) PA5
CS(SPI片选) PF1
RST(复位) PD12
INT(中断输出) PD14
  • 语音识别芯片采用LD3320,与口袋机SPI接口通讯,外接麦克风,外接晶振;通过SPI接口设置LD3320进行初始化和语音识别库的设置,完成特定语音识别
  • 每次识别最多可以设置 50项候选识别句,每个识别句可以是 单字,词组或短句,长度为 不超过10个汉字或者79个字节的拼音串

语音播放MP3文件,需要把MP3文件16进制做成数组(MP3分有ID3头跟无ID3头,这两者不会有影响)

可以用16进制阅读器(比如 UltraEdit(软件+破解在阿里云盘) )打开mp3文件和数组相对照

程序
/*************************main.c*************************/
u8 INT_flag = 1;	//上电触发中断

extern u8 nAsrStatus;
extern u8 nLD_Mode;



//所有中断线都在这,这里只用到14
void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line10) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line10);
	}
	
	if(EXTI_GetITStatus(EXTI_Line11) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line11);
	}
	if(EXTI_GetITStatus(EXTI_Line12) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line12);
	}
	
	if(EXTI_GetITStatus(EXTI_Line13) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line13);
	}
	
	if(EXTI_GetITStatus(EXTI_Line14) != RESET)
	{
		//printf("INT\r\n");
		EXTI_ClearITPendingBit(EXTI_Line14);
		INT_flag = 1;
	}
	
	if(EXTI_GetITStatus(EXTI_Line15) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line15);
	}
}




u8 test_u8;
int main(void)
{
    LCD_ShowString(0, 0, "speech recognition:", 32, TYPEFACE);
	
	GPIO_init_all();//初始化用到的IO端口
	EXTIX_Init();//外部中断初始化
    ld3320_reset();//模块复位
	delay_ms(100);//延时
	
	while(ld3320_check())//检查模块通讯是否正常
	{
		printf("LD3320 Error!!\r\n");//串口打印错误
		delay_ms(500);
	}
	printf("LD3320 OK!!\r\n");//串口打印正常
    
    ld3320_init_asr();//模块设置ASR寄存器
	ld3320_asr_addFixed();//添加识别关键词语
	test_u8 = ld3320_read_reg(0xBF);//看是否是31
	test_u8 = ld3320_asrun();//
	nAsrStatus = LD_ASR_NONE;		//	初始状态:没有在作ASR
	nLD_Mode = LD_MODE_ASR_RUN;//
    
    while(1)
    {
        if(INT_flag)//判断是否有中断
		{
			INT_flag = 0;//中断标志清零
			
			ld3320_process_init();//初始化
			
			test_u8 = LD_GetResult();//查看识别结果
			printf("test_u8 = %02x\r\n",test_u8);//串口打印
			
			LCD_Draw_Rect_Win(0,32,32,32,BACKGROND);//在对应的位置清屏变成蓝色
			LCD_ShowNum(0,32,test_u8,1,32,TYPEFACE);//显示识别的结果   显示0-5之间的数字
			
			
			ld3320_init_asr();//初始化
			ld3320_asr_addFixed();//添加识别关键词语
			test_u8 = ld3320_read_reg(0xBF);//看是否是31
			test_u8 = ld3320_asrun();//
			nAsrStatus = LD_ASR_NONE;		//	初始状态:没有在作ASR
			nLD_Mode = LD_MODE_ASR_RUN;//
	
		}
    }
    
}
/*************************STM32F40x_GPIO_Init.c*************************/
void GPIO_init_all(void)
{
   GPIO_init(GPIO_D,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_PP,GPIO_PuPd_NOPULL);//D14
   GPIO_init(GPIO_D,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//D12
GPIO_init(GPIO_F,GPIO_Pin_1,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//F1
	GPIO_init(GPIO_A,GPIO_Pin_5,GPIO_Mode_IN,GPIO_OType_PP,GPIO_PuPd_NOPULL);//A5
	GPIO_init(GPIO_D,GPIO_Pin_7,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//D7
	GPIO_init(GPIO_A,GPIO_Pin_4,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//A4
}

LD3320.h

//	以下三个状态定义用来记录程序是在运行ASR识别还是在运行MP3播放
# define LD_MODE_IDLE		0x00
# define LD_MODE_ASR_RUN		0x08
# define LD_MODE_MP3		 	0x40


//	以下五个状态定义用来记录程序是在运行ASR识别过程中的哪个状态
# define LD_ASR_NONE				0x00	//	表示没有在作ASR识别
# define LD_ASR_RUNING			0x01	//	表示LD3320正在作ASR识别中
# define LD_ASR_FOUNDOK			0x10	//	表示一次识别流程结束后,有一个识别结果
# define LD_ASR_FOUNDZERO 		0x11	//	表示一次识别流程结束后,没有识别结果
# define LD_ASR_ERROR	 		0x31	//	表示一次识别流程中LD3320芯片内部出现不正确的状态


# define CLK_IN   		   24	//用户需要根据时钟修改这个值
# define LD_PLL_11			(u8)((CLK_IN/2.0)-1)
# define LD_PLL_MP3_19		0x0f
# define LD_PLL_MP3_1B		0x18
# define LD_PLL_MP3_1D   	(u8)(((90.0*((LD_PLL_11)+1))/(CLK_IN))-1)

# define LD_PLL_ASR_19 		(u8)(CLK_IN*32.0/(LD_PLL_11+1) - 0.51)
# define LD_PLL_ASR_1B 		0x48
# define LD_PLL_ASR_1D 		0x1f

// LD芯片固定值
# define        RESUM_OF_MUSIC               0x01
# define        CAUSE_MP3_SONG_END           0x20

# define        MASK_INT_SYNC				0x10
# define        MASK_INT_FIFO				0x04
# define    	   MASK_AFIFO_INT				0x01
# define        MASK_FIFO_STATUS_AFULL		0x08

# define MIC_VOL   0x55  //值越小代表MIC音量越小
# define YUYIN_MAX	5	//语音语句(条)



void EXTIX_Init(void);
void ld3320_reset(void);
u8 ld3320_check(void);
u8 SPI_RW(u8 data);
u8 ld3320_read_reg(u8 add);
void ld3320_write_reg(u8 add, u8 dat);
u8 ld3320_check_asrbusyflag_b2(void);
u8 ld3320_asrun(void);
u8 ld3320_get_result(void);
u8 LD_Check_ASRBusyFlag_b2(void);
void ld3320_init_common(void);
u8 LD_GetResult(void);
void LD_Init_Common(void);
void ld3320_init_asr(void);
u8 ld3320_asr_addFixed(void);
void ld3320_process_init(void);
void LD_AsrStart(void);
u8 ld3320_run_asr(void);

LD3320.c

/*************************LD3320.c*************************/
//枚举
typedef enum
{
    nihao = 1,
    tongxue = 2,
    beijing = 3,
    shanghai = 4,
    bisai = 5
} Order;


u8 nLD_Mode;	//当前模式
u32 nMp3StartPos = 0;
u32 nMp3Pos = 0;
u8 ucStatus;
u32 nMp3Size = 0;

u8 ucRegVal;
u8 nAsrStatus;
u8 ucHighInt;
u8 ucLowInt;

u8 ucSPVol = 15;

u8 bMp3Play;




//D14外部中断初始化
void EXTIX_Init(void)
{
    NVIC_InitTypeDef   NVIC_InitStructure;
    EXTI_InitTypeDef   EXTI_InitStructure;


    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource14);//D14

    EXTI_InitStructure.EXTI_Line = EXTI_Line14;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断请求
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
    EXTI_Init(&EXTI_InitStructure);//配置

    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中断10-15
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);//配置
}

//LD3320芯片复位
void ld3320_reset(void)
{
    RST_H;	//复位引脚拉高
    delay_us(10);	//延时
    RST_L;	//复位引脚拉低
    delay_us(10);	
    RST_H;	//复位引脚拉高
    delay_us(10);

    CS_L;	//片选拉低
    delay_us(10);	//延时
    CS_H;	//片选拉高
    delay_us(10);
}
//模拟SPI时序    发送一个字节并  同时读取一个字节
u8 SPI_RW(u8 data)
{
    u8 i;
    u8 temp;

    temp = 0;

    for(i = 0; i < 8; i++)
    {
        if((data & 0x80) == 0x80)		//要发送的最高位是否是1
        {
            MO_H;			//mosi输出高
        }
        else
        {
            MO_L;			//mosi输出底
        }
        data <<= 1;		//数据左移一位

        delay_us(10);

        SCK_L;

        temp = temp << 1;

        if(READ_MI)	//读取MISO 如果是高
        {
            temp |= 0x01;     	//记录数据
        }

        delay_us(10);

        SCK_H;			//时钟变高
    }
    return temp;					//返回收到的一个字节
}
//读取寄存器的值,0x00是数据可随意,固定是:操作命令---寄存器地址---数据
u8 ld3320_read_reg(u8 add)
{
    u8 temp;

    CS_L;	//必须要在CS低电平时才有效
    SPI_RW(0x05);	//发送读操作命令
    SPI_RW(add);	//发送寄存器地址
    temp = SPI_RW(0x00);
    CS_H;

    return temp;
}
//写入寄存器的值
void ld3320_write_reg(u8 add, u8 dat)
{
    CS_L;
    SPI_RW(0x04);
    SPI_RW(add);
    SPI_RW(dat);
    CS_H;

}
//检查芯片
u8 ld3320_check(void)
{
    u8 a[3];
    memset(a, 0x00, sizeof(a));


    ld3320_reset();
    delay_ms(10);
    ld3320_read_reg(0x06);	//0x06:(只读)FIFO状态
    delay_ms(10);
    a[0] = ld3320_read_reg(0x06);
    delay_ms(10);
    a[1] = ld3320_read_reg(0x35);	//0x35:ADC增益,或可以理解为麦克风(MIC)音量
    delay_ms(10);
    a[2] = ld3320_read_reg(0xb3);	//0xb3:打开或关闭 “语音端点检测”功能

    printf("%02x	", a[0]);	//读取值存储显示
    printf("%02x	", a[1]);
    printf("%02x	", a[2]);
    printf("\r\n");


    ld3320_reset();
    delay_ms(10);
    ld3320_read_reg(0x06);
    delay_ms(10);
    a[0] = ld3320_read_reg(0x06);
    delay_ms(10);
    a[1] = ld3320_read_reg(0x35);
    delay_ms(10);
    a[2] = ld3320_read_reg(0xb3);

    printf("%02x	", a[0]);
    printf("%02x	", a[1]);
    printf("%02x	", a[2]);
    printf("\r\n");



    ld3320_write_reg(0x35, 0x33);	//写入0x33到寄存器地址0x35里
    delay_ms(10);
    ld3320_write_reg(0x1b, 0x55);	//写入0x55到寄存器地址0x1b里	0x1b:时钟频率设置3	
    delay_ms(10);
    ld3320_write_reg(0xb3, 0x40);	//写入0xaa到寄存器地址0xb3里,推荐10~40,越小灵敏度越高容易误判

    a[0] = ld3320_read_reg(0x35);	//读取刚刚写入的值
    delay_ms(10);
    a[1] = ld3320_read_reg(0x1b);	//读取刚刚写入的值
    delay_ms(10);
    a[2] = ld3320_read_reg(0xb3);	//读取刚刚写入的值

    printf("%02x	", a[0]);
    printf("%02x	", a[1]);
    printf("%02x	", a[2]);
    printf("\r\n");

    if(a[0] != 0x33 || a[1] != 0x55 || a[2] != 0x20)	//检查写入的值是否一样
        return 1;
    return 0;
}
//通用初始化
void LD_Init_Common(void)
{
    bMp3Play = 0;

    ld3320_read_reg(0x06);
    ld3320_write_reg(0x17, 0x35);
    delay_ms(10);
    ld3320_read_reg(0x06);

    ld3320_write_reg(0x89, 0x03);	//模拟电路控制 初始化时写 03H MP3播放时写 FFH
    delay_ms(5);
    ld3320_write_reg(0xCF, 0x43);	//内部省电模式设置 初始化时写入 43H MP3初始化和ASR初始化时写入 4FH
    delay_ms(5);
    ld3320_write_reg(0xCB, 0x02);	//ASR:读取ASR结果


    ld3320_write_reg(0x11, LD_PLL_11);	//时钟频率设置1
    if (nLD_Mode == LD_MODE_MP3)
    {
        ld3320_write_reg(0x1E, 0x00);	//ADC专用控制,应初始化为00H
        ld3320_write_reg(0x19, LD_PLL_MP3_19);	//时钟频率设置2
        ld3320_write_reg(0x1B, LD_PLL_MP3_1B);	//时钟频率设置3
        ld3320_write_reg(0x1D, LD_PLL_MP3_1D);	//时钟频率设置4
    }
    else
    {
        ld3320_write_reg(0x1E, 0x00);	
        ld3320_write_reg(0x19, LD_PLL_ASR_19);
        ld3320_write_reg(0x1B, LD_PLL_ASR_1B);
        ld3320_write_reg(0x1D, LD_PLL_ASR_1D);
    }
    delay_ms(10);

    ld3320_write_reg(0xCD, 0x04);	//初始化时写入04H 允许DSP休眠
    ld3320_write_reg(0x17, 0x4c);
    delay_ms(5);
    ld3320_write_reg(0xB9, 0x00);	//当前添加识别句的字符串长度(拼音字符串)初始化时写入00H 每添加一条识别句后要设定一次。
    ld3320_write_reg(0xCF, 0x4F);
    ld3320_write_reg(0x6F, 0xFF);	//对芯片进行初始化时设置为0xFF
}


void ld3320_init_asr(void)
{
    nLD_Mode = LD_MODE_ASR_RUN;	//运行ASR识别
    //ld3320_init_common();
    LD_Init_Common();

    ld3320_write_reg(0xBD, 0x00);	//初始化控制寄存器 02H:启动MP3模块 00H:语音识别模块
    ld3320_write_reg(0x17, 0x48);	//48H:激活DSP 4CH:使DSP休眠,比较省电 35H:对LD3320进行软复位
    delay_ms(10);

    ld3320_write_reg(0x3C, 0x80);	//FIFO_EXT下限低8位
    ld3320_write_reg(0x3E, 0x07);	//FIFO_EXT下限高8位
    ld3320_write_reg(0x38, 0xff);	//FIFO_EXT上限低8位
    ld3320_write_reg(0x3A, 0x07);	//FIFO_EXT上限高8位


    ld3320_write_reg(0x40, 0);	//FIFO_EXT MCU水线低8位
    ld3320_write_reg(0x42, 8);	//FIFO_EXT MCU水线高8位
    ld3320_write_reg(0x44, 0);	//FIFO_EXT DSP水线低8位
    ld3320_write_reg(0x46, 8);	//FIFO_EXT DSP水线高8位
    delay_ms(10);
}
//检查是否处于闲状态
u8 ld3320_check_asrbusyflag_b2(void)
{
    u8 j;
    u8 flag = 0;
    for (j = 0; j < 10; j++)
    {
        if (ld3320_read_reg(0xb2) == 0x21)	//ASR:DSP忙闲状态,0x21 表示闲,查询到为闲状态可以进行下一步ASR动作
        {
            flag = 1;
            break;
        }
        delay_ms(10);
    }
    return flag;
}
// Return 1: success.
//	添加识别关键词语,开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中关于垃圾词语吸收错误的用法
u8 ld3320_asr_addFixed(void)
{
    u8 k, flag;
    u8 nAsrAddLength;
    const char sRecog[YUYIN_MAX][30] =
    {
        "ni hao",
        "tong xue",
        "bei jing",
        "shang hai",
        "bi sai"
    };	//需要识别的拼音
	//编号可以相同,可以不连续 例子中的“北京”和“首都”对应同一编号,说这两个词会有相同的结果返回
    const Order pCode[YUYIN_MAX] =
    {
        nihao,
        tongxue,
        beijing,
        shanghai,
        bisai
    };	//编号

    flag = 1;

    for (k = 0; k < YUYIN_MAX; k++)
    {
        if(ld3320_check_asrbusyflag_b2() == 0)
        {
            //printf("busy------------\r\n");
            flag = 0;
            break;
        }
        else
        {
            //printf("not busy------------\r\n");
        }

        ld3320_write_reg(0xc1, pCode[k] );	//ASR:识别字Index(00H—FFH)
        ld3320_write_reg(0xc3, 0 );	//ASR:识别字添加时写入00
        ld3320_write_reg(0x08, 0x04);	//清除FIFO内容(清除指定FIFO后再写入一次00H) 第0位:写入1→清除FIFO_DATA 第2位:写入1→清除FIFO_EXT
        delay_ms(10);
        ld3320_write_reg(0x08, 0x00);	//清除指定FIFO后再写入一次00H
        delay_ms(10);

        for (nAsrAddLength = 0; nAsrAddLength < 20; nAsrAddLength++)
        {
            if (sRecog[k][nAsrAddLength] == 0)
            {
                break;
            }
            ld3320_write_reg(0x5, sRecog[k][nAsrAddLength]);	//FIFO_EXT数据口
        }
        ld3320_write_reg(0xb9, nAsrAddLength);	//当前添加识别句的字符串长度
        ld3320_write_reg(0xb2, 0xff);	//写入忙状态
        ld3320_write_reg(0x37, 0x04);	//语音识别控制命令下发寄存器 写04H:通知DSP要添加一项识别句。 写06H:通知DSP开始识别语音。 在下发命令前,需要检查B2寄存器的状态
    }
    return flag;
}

u8 ld3320_asrun(void)
{
    u8 temp;

    temp = temp;

    ld3320_write_reg(0x35, MIC_VOL);	//ADC增益,或可以理解为麦克风(MIC)音量。
    ld3320_write_reg(0x1C, 0x09);	//ADC开关控制写09H Reserve保留命令字
    ld3320_write_reg(0xBD, 0x20);	//初始化控制寄存器
    ld3320_write_reg(0x08, 0x01);	//写入20H;Reserve保留命令字
    delay_ms(10);
    ld3320_write_reg(0x08, 0x00);	//清除FIFO内容(清除指定FIFO后再写入一次00H)
    delay_ms(10);

    if(ld3320_check_asrbusyflag_b2() == 0)
    {
        return 0;
    }

    ld3320_write_reg(0xB2, 0xff);	//DSP忙闲状态
    ld3320_write_reg(0x37, 0x06);	//写06H:通知DSP开始识别语音
    delay_ms(10);

    temp = ld3320_read_reg(0xBF);//看是否是32-3a 读到数值为0x35,可以确定是一次语音识别流程正常结束


    ld3320_write_reg(0x1C, 0x0b);	//写0BH 麦克风输入ADC通道可用
    ld3320_write_reg(0x29, 0x10);	//中断允许

    ld3320_write_reg(0xBD, 0x00);	//写入00H;然后启动;为ASR模块;
    return 1;
}
//获取识别最佳结果
u8 LD_GetResult(void)
{
    return ld3320_read_reg(0xc5);	//读取ASR结果(最佳)
}
void ld3320_process_init(void)
{
    u8 nAsrResCount = 0;
    u8 temp;

    ucRegVal = ld3320_read_reg(0x2B);	//中断请求编号
    //printf("ucRegVal = %02x\r\n",ucRegVal);

    ucHighInt = ld3320_read_reg(0x29);	//中断允许
    //printf("ucHighInt = %02x\r\n",ucHighInt);

    ld3320_write_reg(0x29, 0) ;
    ucLowInt = ld3320_read_reg(0x02);	//FIFO中断允许
    //printf("ucLowInt = %02x\r\n",ucLowInt);


    ld3320_write_reg(0x02, 0) ;
    if(nLD_Mode == LD_MODE_ASR_RUN)
    {
		//ucRegVal==0x10表示语音识别有结果产生且芯片内部FIFO中断发生
		//读到数值为0x35,可以确定是一次语音识别流程正常结束
		//0x21表示空闲
        if( (ucRegVal & 0x10) && ld3320_read_reg(0xbf) == 0x35 && ld3320_read_reg(0xb2) == 0x21)
        {
			//中断辅助信息
			//ASR:中断时,是语音识别有几个识别候选 Value: 1 – 4: N个识别候选 0或者大于4:没有识别候选
            nAsrResCount = ld3320_read_reg(0xba);	
            //printf("nAsrResCount = %02x\r\n",nAsrResCount);


            if(nAsrResCount > 0 && nAsrResCount < 4)
            {
                nAsrStatus = LD_ASR_FOUNDOK;	//表示一次识别流程结束后,有一个识别结果

                temp = LD_GetResult();
                printf("temp = %02x\r\n", temp);
            }
            else
            {
                nAsrStatus = LD_ASR_FOUNDZERO;	//表示一次识别流程结束后,没有识别结果
            }
        }
        else
        {
            nAsrStatus = LD_ASR_FOUNDZERO;	////表示一次识别流程结束后,没有识别结果
        }
        ld3320_write_reg(0x2b, 0);	//清除中断标志
        ld3320_write_reg(0x1C, 0);	//ADC开关控制 写00H ADC不可用

        return;
    }
}

摄像头

摄像头型号OV2640,I2C接口对摄像头进行设置

所用模块 连接注意
STM32模块扩展板 插口袋机右边
语音识别模块 模块有灯的在右下角

硬件连接:

管脚号 对应扩展板管脚
Y0(8位数据输出) PE13
Y1 PF15
Y2 PE15
Y3 PE14
Y4 PB13
Y5 PA4
Y6 PD7
Y7 PA5
SDA(I2C数据) PE14
SCL(I2C时钟) PD15
VSYNC(帧同步输出) PD14
PWND(掉电模式使能) PD12
PCLK(像素时钟) PE1
/*************************STM32F40x_GPIO_Init.h*************************/
# define READ_SDA		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_14)

# define READ_VSYNC		GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_14)

# define READ_HREF		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_1)

# define READ_PCLK		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_1)

# define READ_Y0		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_13)
# define READ_Y1		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_15)
# define READ_Y2		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_15)
# define READ_Y3		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_14)
# define READ_Y4		GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)
# define READ_Y5		GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)
# define READ_Y6		GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_7)
# define READ_Y7		GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)

# define SCL_L		(GPIOD->BSRRH = GPIO_Pin_15)
# define SCL_H		(GPIOD->BSRRL = GPIO_Pin_15)

# define SDA_L		(GPIOF->BSRRH = GPIO_Pin_14)
# define SDA_H		(GPIOF->BSRRL = GPIO_Pin_14)

# define PWDN_L		(GPIOD->BSRRH = GPIO_Pin_12)
# define PWDN_H		(GPIOD->BSRRL = GPIO_Pin_12)


# define OV2640_DATA1   	GPIOA->IDR
# define OV2640_DATA2   	GPIOB->IDR
# define OV2640_DATA3   	GPIOD->IDR
# define OV2640_DATA4   	GPIOE->IDR
# define OV2640_DATA5   	GPIOF->IDR



//IO方向设置
# define SCCB_SDA_IN()  {GPIOF->MODER&=(u32)(~(3<<(14*2)));GPIOF->MODER|=(u32)(0<<14*2);}	//PF14 输入
# define SCCB_SDA_OUT() {GPIOF->MODER&=(u32)(~(3<<(14*2)));GPIOF->MODER|=(u32)(1<<14*2);} 	//PF14 输出
/*************************STM32F40x_GPIO_Init.c*************************/

void GPIO_init_all(void)
{
	GPIO_init(GPIO_E,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//SDA
	GPIO_init(GPIO_D,GPIO_Pin_15,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);	//SCL
	
	GPIO_init(GPIO_D,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//VSYNC
	GPIO_init(GPIO_D,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);	//PWND
	GPIO_init(GPIO_F,GPIO_Pin_1,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);		//HREF
	
	GPIO_init(GPIO_A,GPIO_Pin_5,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);		//Y7
	GPIO_init(GPIO_D,GPIO_Pin_7,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);		//Y6
	GPIO_init(GPIO_A,GPIO_Pin_4,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);		//Y5
	GPIO_init(GPIO_E,GPIO_Pin_15,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//Y2
	GPIO_init(GPIO_F,GPIO_Pin_15,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//Y1
	GPIO_init(GPIO_E,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//Y3
	GPIO_init(GPIO_E,GPIO_Pin_13,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//Y0
	GPIO_init(GPIO_B,GPIO_Pin_13,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//Y4
	
	GPIO_init(GPIO_E,GPIO_Pin_1,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//PCLK
}

main.c

/*************************main.c(I2C部分)*************************/
//SCCB起始信号
//当时钟为高的时候,数据线的高到低,为SCCB起始信号
//在激活状态下,SDA和SCL均为低电平
void SCCB_Start(void)
{
    SDA_H;     //数据线高电平	   
    SCL_H;	    //在时钟线高的时候数据线由高至低
    delay_us(50);  
    SDA_L;
    delay_us(50);	 
    SCL_L;	    //数据线恢复低电平,单操作函数必要	  
}



//SCCB停止信号
//当时钟为高的时候,数据线的低到高,为SCCB停止信号
//空闲状况下,SDA,SCL均为高电平
void SCCB_Stop(void)
{
    SDA_L;
    delay_us(50);	 
    SCL_L;	
    delay_us(50); 
    SDA_H;	
    delay_us(50);
} 



//产生NA信号
void SCCB_No_Ack(void)
{
	delay_us(50);
	SDA_H;	
	SCL_H;	
	delay_us(50);
	SCL_L;	
	delay_us(50);
	SDA_L;	
	delay_us(50);
}




//SCCB,写入一个字节
//返回值:0,成功;1,失败. 
u8 SCCB_WR_Byte(u8 dat)
{
	u8 j,res;	 
	for(j=0;j<8;j++) //循环8次发送数据
	{
		if(dat&0x80)SDA_H;	
		else SDA_L;
		dat<<=1;
		delay_us(50);
		SCL_H;	
		delay_us(50);
		SCL_L;		   
	}			 
	SCCB_SDA_IN();		//设置SDA为输入 
	delay_us(50);
	SCL_H;			//接收第九位,以判断是否发送成功
	delay_us(50);
	if(READ_SDA)res=1;  //SDA=1发送失败,返回1
	else res=0;         //SDA=0发送成功,返回0
	SCL_L;		 
	SCCB_SDA_OUT();		//设置SDA为输出    
	return res;  
}	 




//SCCB 读取一个字节
//在SCL的上升沿,数据锁存
//返回值:读到的数据
u8 SCCB_RD_Byte(void)
{
	u8 temp=0,j;    
	SCCB_SDA_IN();		//设置SDA为输入  
	for(j=8;j>0;j--) 	//循环8次接收数据
	{		     	  
		delay_us(50);
		SCL_H;
		temp=temp<<1;
		if(READ_SDA)temp++;   
		delay_us(50);
		SCL_L;
	}	
	SCCB_SDA_OUT();		//设置SDA为输出    
	return temp;
} 

其他代码略(源码在阿里云网盘)

超声波测距

硬件连接:

所用模块 连接注意
STM32模块扩展板 插口袋机右边
超声波模块 模块有灯的在左上角

使用两个超声波探头,一个发送,一个接收; CS100A 是超声波测距控制芯片

/*************************STM32F40x_Timer_eval.c*************************/
__IO uint16_t Tim3_Cont_val = 0;


extern u32 t_count;

//定时器3
void Timer3_Config(uint16_t period, uint16_t prescaler)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
	
  /* TIM3 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = period;
  TIM_TimeBaseStructure.TIM_Prescaler = prescaler;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

	/* TIM3 enable */
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

  /* TIM3 enable counter */
  TIM_Cmd(TIM3, ENABLE);
	
//	/* Configure and enable TIM3 interrupt */
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
//定时器中断函数
void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
  {
    /* Clear interrupt pending bit */
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
		t_count ++;
  }
}
/*************************main.c*************************/
//外部中断服务程序
void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line14) != RESET)
	{
		read_bit = READ_ECHO;//读端口电平
		if(read_bit==1)//如果是1 计数清零
		{
			t_count = 0;
		}
		else		//如果是0  停止计数   保存计数值
		{
			race_count = t_count;
			race_ok_bit = 1;
		}
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
}




//外部中断初始化函数
void EXTIX_Init(void)
{
  NVIC_InitTypeDef   NVIC_InitStructure;
	EXTI_InitTypeDef   EXTI_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
	
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource14);//E14

	EXTI_InitStructure.EXTI_Line = EXTI_Line14;
	
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //上升沿下降沿 都触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
  EXTI_Init(&EXTI_InitStructure);//配置
 

	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中断10-15
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);//配置

}

int main(void)
{
    Timer3_Config(20,19);//5us 21*20/42000000=10us
    
      while (1)
  {
		TRIG_H;				//发射管教变高
		delay_us(10);	//延时
		TRIG_L;				//发射管教变低
		
		DIS_mm_f = (float)race_count;//读取计数
		
		DIS_mm_f = DIS_mm_f * 5.0f * 0.34f / 2.0f;//计算
		
		DIS_mm_u16 = DIS_mm_f/10;//变成整形
		
		
		
		LCD_Draw_Rect_Win(144,0,80,32,BACKGROND);//刷新显示
		LCD_ShowNum(144,0,DIS_mm_u16,5,32,TYPEFACE);//显示数据
		
		
		if(race_ok_bit==1)//是否收到返回的脉冲
		{
			race_ok_bit = 0;
			
			printf("%.1f\r\n",DIS_mm_u16);//串口打印
		}
		else
		{
			printf("no_race\r\n");//串口打印
		}
		
		
		delay_ms(50);//间隔50ms采集一次
		
  }
}
/*************************STM32F40x_GPIO_Init.c*************************/
void GPIO_init_all(void)
{
GPIO_init(GPIO_E,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_NOPULL);//E14
	GPIO_init(GPIO_E,GPIO_Pin_13,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//E13
}

提供的函数详解

代码集合

/*************************STM32F40x_GPIO_Init.c*************************/
void GPIO_init(u16 GPIOx, u16 GPIO_Pin, GPIOMode_TypeDef mode, GPIOOType_TypeDef type, GPIOPuPd_TypeDef pupd)
{

//宏定义端口组	
# define GPIO_A	0x0000
# define GPIO_B	0x0001
# define GPIO_C	0x0002
# define GPIO_D	0x0003
# define GPIO_E	0x0004
# define GPIO_F	0x0005
# define GPIO_G	0x0006
	
	
	GPIO_InitTypeDef        GPIO_InitStructure;
	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin;	//引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度
	GPIO_InitStructure.GPIO_Mode = mode;	//操作模式
	GPIO_InitStructure.GPIO_OType = type;	//输出类型
	GPIO_InitStructure.GPIO_PuPd = pupd;	//是否上拉/下拉
	
	
	switch(GPIOx)
	{
		case GPIO_A:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
			GPIO_Init(GPIOA,&GPIO_InitStructure);
			break;
		
		case GPIO_B:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
			GPIO_Init(GPIOB,&GPIO_InitStructure);
			break;
		
		case GPIO_C:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
			GPIO_Init(GPIOC,&GPIO_InitStructure);
			break;
		
		case GPIO_D:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
			GPIO_Init(GPIOD,&GPIO_InitStructure);
			break;
		
		case GPIO_E:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
			GPIO_Init(GPIOE,&GPIO_InitStructure);
			break;
		
		case GPIO_F:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
			GPIO_Init(GPIOF,&GPIO_InitStructure);
			break;
		
		case GPIO_G:
			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
			GPIO_Init(GPIOG,&GPIO_InitStructure);
			break;
		
	}
}	
/*************************STM32F40x_LCD_SPI.c*************************/
一个字符占16*32(大字体),16*16(小字体)
//显示字符串
void LCD_ShowString(uint16_t x, uint16_t y, char *p, uint8_t size, uint16_t Color)
● 参数1:x横坐标,0~240
● 参数2:y纵坐标才,0~320
● 参数3:要显示的字符串
● 参数4:显示大小(16是小字体,32是大字体)
● 参数5:颜色    
    
/*************************STM32F40x_Usart_eval.c(USART5部分)*************************/    
char usart5_race_buf[100];
u16 usart5_race_count;

u8 usart5_race_over_bit;

void uart5_race_buf_init(void)
{
	u16 i;
	
	for(i=0;i<100;i++)
	{
		usart5_race_buf[i] = 0;
	}
}
//串口5初始化
void Uart5_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	

  /* Enable GPIO clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	
  /* Connect PXx to USARTx_Tx*/
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5);
	
  /* Connect PXx to USARTx_Rx*/
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5);
	
	/* Configure USART Tx as alternate function  */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;	//PC12
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Configure USART Rx as alternate function  */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;	//PD2
  GPIO_Init(GPIOD, &GPIO_InitStructure);
	

	
  NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
	
	
  /* Enable USART clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);//串口5的时钟使能
	
	//USART5 初始化设置
	USART_InitStructure.USART_BaudRate = 115200;//一般设置为115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8bit
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位1位
	USART_InitStructure.USART_Parity = USART_Parity_No;//不使用奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//不使用硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	
 
  USART_Init(UART5, &USART_InitStructure);	//初始化USART5
  
	USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);//开启接收中断
	
 
  USART_Cmd(UART5, ENABLE);	//使能USART5
	
  USART_ClearFlag(UART5, USART_FLAG_TC);	//清除发送完成标志位
}


//串口5发送数组  dat是数组   len是长度
void Uart5_send_data(char *dat, unsigned short len)
{
	usart5_race_count = 0;
	uart5_race_buf_init();//接收数组清零
	
	while(len--)
	{
        //stm32中DR寄存器只有低9位有效,其余高位是保留的,为了保证正确性和将来的兼容性,只取Data的低9位数据,所以&(uint16_t)0x01FF
		UART5->DR = (*dat & (uint16_t)0x01FF);
		while(USART_GetFlagStatus(UART5, USART_FLAG_TC) == RESET);//传输完成
		dat++;
	}
}



//串口5接收中断
void UART5_IRQHandler(void)
{
	unsigned char Res;
	
	if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)  //接收中断
	{
		Res = USART_ReceiveData(UART5);//(UART5->DR);	//读取接收到的数据
		
		
		usart5_race_buf[usart5_race_count] = Res;
		usart5_race_count++;
		if(usart5_race_count==100)
		{
			usart5_race_count = 0;
			usart5_race_over_bit = 1;
		}
  }
}
//重定向printf到USART1
int fputc(int ch, FILE *f)
{
	USART1->DR = (ch & (uint16_t)0x01FF);
  while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

  return ch;
}

口袋机端口

  • PD7 接口 连接了内部RAM的使能, 外部是不可以使用的,必须输出高电平,否则会影响其他端口的电平

  • 左侧是口袋机原理图的 P3,00 ~ 29 对应网络标号 GPIO_30 ~ GPIO_59;右 侧是口袋机原理图的 P1,00 ~ 29 对应网络标号 GPIO_00 ~ GPIO_29

  • 口袋机按键(初始化为输入,开漏,上拉),低电平时表示按下

按键(管脚) 对应程序
A(PA^0) BUTTON_A
READ_BUTTON4
B(PG^6) BUTTON_B
READ_BUTTON3
C(PG^7) BUTTON_C
READ_BUTTON2
D(PG^8) BUTTON_D
READ_BUTTON1
上(PG^11) BUTTON_UP
READ_BUTTON7
下(PD^6) BUTTON_DOWN
READ_BUTTON6
左(PG^10) BUTTON_LEFT
READ_BUTTON8
右(PD^3) BUTTON_RIGHT
READ_BUTTON5
  • 串口1(波特率9600),拿来单片机与电脑通信
USART1(管脚)
PA^9(USB_UART_Rx)
PA^10(USB_UART_Tx)

其他注意问题

  • 在使用串口中断函数处理数据时,不用调用函数 USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG) 清除发送完成标志位,否则程序可能会发生异常混乱
  • STM32F4系列 — TIM1,TIM8,TIM11 时钟频率是APB2两分频的2倍时钟频率(即168MHz); TIM2~TIM7, TIM12~TIM14 时钟频率是APB1四分频的2倍频率(即84MHz);所以定时时需要注意预分频值取 167还是83
  • BSRRH 表示BSRR寄存器高16位(BRy),哪一个BRy置1,引脚输出 低电平BSRRL 表示BSRR寄存器低16位(BSy),哪一个BRy置1,引脚输出 高电平

硬件注意问题

  • 小车 12V 充电器;垃圾桶 13.8V 充电器
  • 如果小车夹子没反应查看一下线是否松了(如果代码正常的话)

函数问题(第一次用的)

函数 函数作用 注意
GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF) 更改指定引脚的映射 不要用 “或” 一次多选引脚;要分开写
第二个参数是 GPIO_PinSourcex,不要写成 GPIO_Pin_x,它们是有区别的
参数3是选择用作备用功能的引脚
  • 外部中断的一般配置步骤

1、使能SYSCFG时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYACFG,ENABLE);

2、初始化IO口为输入:GPIO_Init();(中断与IO口电平无关)

3、设置IO口与中断线的映射关系:void SYSCFG_EXTILineConfig();

4、初始化线上中断,设置触发条件:EXTI_init();

5、配置中断分组(NVIC),并使能中断:NVIC_Init();

6、编写中断服务函数:EXTIx_IRQHandler();

7、清除中断标志位:EXTI_ClearITPendingBit();

  • 使用定时器的外部时钟 ETR 的方式实现,从ETR输入方波作为外部时钟,输出PWM波触发ADC采集(用来对引脚上的输入信号进行统计)。定时时间计算与内部时钟时一样计算,只是 总时钟频率变化了,在检测验证时要注意计算输出的频率,避免乱设置导致频率太小,计数时用外部时钟模式2
  • GPIOG->ODR 是直接操作寄存器(对ODR赋值是一次操作16位的,也就是同时设置了16个引脚的输出电平); IDR 是查看引脚电平状态用的寄存器(只读), ODR 是引脚电平输出的寄存器
  • 清除空闲中断方法:USART的状态位TC是置位的,TC位的置零则是通过软件序列来清除的,具体的步骤是“ 先读USART_SR,然后写入USART_DR ”,只有这样才能够清除标志位TC
  • TIM1做计数时的中断通道是:TIM1_UP_TIM10_IRQn(TIM1 更新中断和 TIM10 全局中断)

各设备通信全局定义

单元 ID
转盘 1
小车 2
集中站 3
垃圾桶1 4
垃圾桶2 5
垃圾桶3 6
垃圾桶4 7

沙盘存在的问题汇总

  • 轮子到第2个转弯会打滑几秒
  • 夹子夹不准(现在夹到的几率有80%)
  • 轮子的PWM看不懂频率是初始化为多少,根据计算结果不太准(待解决)
  • 在小车没回到原点时要是有垃圾进入托盘小车回到原点后不会去夹,需要在小车在原点再去放垃圾到转盘才行
  • 转盘识别有时候傻傻的,纸皮被识别成厨余垃圾也是醉了

<1>小车部分

小车代码详解(主要代码)

TIM5/车轮编码器

  • 编码器管脚
管脚 对应 模式 哪个轮 类型
PA^5 CODE-F-L-1 输入,开漏,上拉 前左 编码器
PB^1(TIM3_CH4) CODE-F-L-2 / / /
PD^14 CODE-F-R-1 同上 前右 编码器
PD^15(TIM4_CH4) CODE-F-R-2 / / /
PC^8 CODE-B-L-1 同上 后左 编码器
PC^10(UART4_Tx) CODE-B-L-2 / / /
PC^12(UART5_Tx) CODE-B-R-1 同上 后右 编码器
PC^11(UART4_Rx) CODE-B-R-2 / / /
  • 轮子用到的PWM(使用TIM1的通道)
管脚 对应 频率 哪个轮 类型
PE^9 TIM1_CH1 前左 PWM
PE^11 TIM1_CH2 前右 PWM
PE^14 TIM1_CH4 后左 PWM
PE^13 TIM1_CH3 后右 PWM
  • 方向控制端口
管脚 哪个轮 模式 类型
PB^15 前左 输出,推挽,无上下拉 方向控制
PB^8 前右 同上 方向控制
PG^13 后左 同上 方向控制
PG^12 后右 同上 方向控制
  • B表示向后走,F表示向前
/*************************CAR.h*************************/
# define MOTO_0_B		(GPIOB->BSRRH = GPIO_Pin_15)  // 复位 置0 前左
# define MOTO_0_F		(GPIOB->BSRRL = GPIO_Pin_15)  //置1  默认向前转

# define MOTO_1_B		(GPIOB->BSRRH = GPIO_Pin_8)	//前右
# define MOTO_1_F		(GPIOB->BSRRL = GPIO_Pin_8)

# define MOTO_2_B		(GPIOG->BSRRH = GPIO_Pin_13)	//后左
# define MOTO_2_F		(GPIOG->BSRRL = GPIO_Pin_13)

# define MOTO_3_B		(GPIOG->BSRRH = GPIO_Pin_12)	//后右
# define MOTO_3_F		(GPIOG->BSRRL = GPIO_Pin_12)
/*************************car_init.c*************************/
void encoder_gpio_init(void)
{

    EXTIX_Init_A5();	//前左
    EXTIX_Init_D14();	//前右
    EXTIX_Init_C8();	//后左
    EXTIX_Init_C12();	//后右
}
//PA5  中断初始化  用于四个电机的编码器 前左(其余3个配置一样只是中断线不一样)
void EXTIX_Init_A5(void)
{
    NVIC_InitTypeDef   NVIC_InitStructure;	//定义结构体
    EXTI_InitTypeDef   EXTI_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);		//中断分组2
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);	//使用外部中断必须要使能的时钟
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource5);	//把A组引脚5设置为外部中断5
	
    EXTI_InitStructure.EXTI_Line = EXTI_Line5;	//5号线
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;	//使能中断
    EXTI_Init(&EXTI_InitStructure);	//初始化

    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;	//中断通道选择
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;	//抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;	//子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//使能通道
    NVIC_Init(&NVIC_InitStructure);	//初始化

    GPIO_init(GPIO_A, GPIO_Pin_5, GPIO_Mode_IN, GPIO_OType_OD, GPIO_PuPd_UP);	//输入,开漏,上拉
}

//底盘电机 初始化
void moto_car_init(void)
{
    TIM1_CH1_PWM_Init_PE9(1000, 8); //前左轮的 PWM初始化
    TIM1_CH2_PWM_Init_PE11(1000, 8); //前右的 PWM初始化
    TIM1_CH4_PWM_Init_PE14(1000, 8); //后左的 PWM初始化
    TIM1_CH3_PWM_Init_PE13(1000, 8); //后右的 PWM初始化

    GPIO_init(GPIO_B, GPIO_Pin_15, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //前左轮的转动方向控制端口初始化
    GPIO_init(GPIO_B, GPIO_Pin_8, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //前右
    GPIO_init(GPIO_G, GPIO_Pin_13, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //后左
    GPIO_init(GPIO_G, GPIO_Pin_12, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //后右

    MOTO_0_F;//前左轮 默认向前转
    MOTO_1_F;//前右
    MOTO_2_F;//后左
    MOTO_3_F;//后右

    TIM_SetCompare1(TIM1, 0); //0-100   前左轮 PWM输出设置为0  先不转
    TIM_SetCompare2(TIM1, 0); //0-100		前右轮
    TIM_SetCompare4(TIM1, 0); //0-100		后左轮
    TIM_SetCompare3(TIM1, 0); //0-100		后右轮
}
//TIM1_CH3   PE13   PWM    前左轮
//参数:1000, 8
//剩下3个配置也一样管脚不一样而已
void TIM1_CH3_PWM_Init_PE13(u32 arr, u32 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;	//定义结构体
    TIM_OCInitTypeDef TIM_OCInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);	//使能TIM1时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);	//使能E组时钟

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推挽
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;	//PE^13
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	//无上下拉
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);	//把PE^13复用为TIM1

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//分频因子
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
    TIM_TimeBaseStructure.TIM_Prescaler = psc;	//预分频系数
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;	//默认
    TIM_TimeBaseStructure.TIM_Period = arr;	//重装载值

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);	//初始化

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	//PWM1模式 CNT < CCR1为有效电平
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出比较模式
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//使能互补输出
    TIM_OCInitStructure.TIM_Pulse = (arr + 1) / 2;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出比较极性高
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;// 互补输出极性高
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;//MOE=0 设置TIM1输出比较空闲
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;//MOE=0 重置TIM1输出比较空闲状态

    TIM_OC3Init(TIM1, &TIM_OCInitStructure);
    TIM_Cmd(TIM1, ENABLE);

    TIM_CtrlPWMOutputs(TIM1, ENABLE);	//高级定时器必须要加
}
/*************************STM32F40x_Timer_eval.c*************************/
//TIM5中断服务函数
void TIM5_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)	//当触发中断
    {
        TIM_ClearITPendingBit(TIM5, TIM_IT_Update);	//清除标志位
        //PG9_L;
        timer_interrupt_fun();
        //PG9_H;
    }
}
/*************************Car.c*************************/
//定时器5中断要执行的函数
void timer_interrupt_fun(void)//定时器中需要运行的函数
{
    timer5_speed_calculation_fun();//计算4个轮子的速度  和 求平均后 计算小车走的距离

    if(Tracing_dis_bit)//是否需要寻迹走
    {
        timer_Tracing_fun();//寻迹走函数
        chack_MAG_fun();		//检查是否有十字磁条或者左转磁条
    }

    if(speed_PID_bit)//是否需要4个轮子的速度PID
    {
        timer5_speed_0_pid_fun(speed_set_mun_L);
        timer5_speed_1_pid_fun(speed_set_mun_R);
        timer5_speed_2_pid_fun(speed_set_mun_L);
        timer5_speed_3_pid_fun(speed_set_mun_R);
    }


}
//在定时器5中断中执行的函数  记录转速  记录路程
void timer5_speed_calculation_fun(void)
{
    u8 i;

    speed_avg = 0;
    for(i = 0; i < 4; i++)
    {
        speed_actual[i] = speed_count[i];//记录速度     speed_count是在编码器中断中累加的
        speed_count[i] = 0;//一个计数周期清零

        speed_avg = speed_avg + speed_actual[i];//速度求和
    }

    speed_avg = speed_avg / 4;//速度求平均

    distance_now_u32 = distance_now_u32 + speed_avg;//累计走形距离    distance_now_u32是小车走的距离计数
}

磁条传感器/走+夹过程

管脚 对应 模式
PF^2 MAG_1 输入,推挽,无上下拉
PD^9 MAG_2 同上
PF^4 MAG_3 同上
PF^3 MAG_4 同上
PB^13 MAG_5 同上
PF^1 MAG_6 同上
PE^1 MAG_7 同上
PD^12 MAG_8 同上
/*************************Car_init.c*************************/
//磁条传感器端口初始化
void gpio_MAG_init_all(void)
{
    GPIO_init(GPIO_F, GPIO_Pin_2, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //F2
    GPIO_init(GPIO_D, GPIO_Pin_9, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //D9
    GPIO_init(GPIO_F, GPIO_Pin_4, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //F4
    GPIO_init(GPIO_F, GPIO_Pin_3, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //F3
    GPIO_init(GPIO_B, GPIO_Pin_13, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //B13
    GPIO_init(GPIO_F, GPIO_Pin_1, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //F1
    GPIO_init(GPIO_E, GPIO_Pin_1, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //E1
    GPIO_init(GPIO_D, GPIO_Pin_12, GPIO_Mode_IN, GPIO_OType_PP, GPIO_PuPd_NOPULL); //D12
}
  • 4个托盘的距离+4个集中站距离
  • Tracing_to_cross_and_dis_fun 函数:走过十字磁条后再走一段距离,参数 tuopan_dis[x] 是第几个托盘的距离(x分别是0,1,2,3);前提必须是在起点,不能在任意一个托盘前再执行走到另一个托盘
  • car_move_back_fun 函数:小车后退多少个距离
  • Tracing_to_L_fun 函数:走到第一个左转(前3个转弯的参数都是1,第4个转弯参数是3,这样会回到原点)
  • car_turn_L_fun 函数:左转90度
  • find_MAG_fun 函数:转弯后车身修正(防止偏离路线)
  • arm_0_L 函数:整个机械臂左转多少脉冲(参数是数字,一个个试);arm_0_R 函数:整个机械臂右转多少脉冲(参数是数字,一个个试)
  • arm_1_F 函数:大臂向前多少脉冲;arm_1_B 函数:大臂向后多少脉冲
  • arm_2_DN 函数:小臂向前多少脉冲;arm_2_UP 函数:小臂向后多少脉冲
  • 关于夹子夹托盘的东西需要考虑 走到托盘的距离 + 底盘转的角度 + 大小臂的角度 + 激光扫描后的向右偏移多少的问题(有可能第一个托盘可以但是到第4个托盘参数就会不适用就得又改,改到通用4个托盘为止,也可以用判断语句每个托盘的参数都不一样用if进入)

测试代码:

//测试走到设定托盘夹完东西走一圈到原点(不丢到垃圾桶)		
Tracing_to_cross_and_dis_fun(tuopan_dis[3]);
arm_pick_up_block_fun();
car_move_back_fun(500);//小车后退300个距离
Tracing_to_L_fun(1);//走到第一个左转
car_turn_L_fun();//自己左转90度
find_MAG_fun();//小车车身修正磁条
Tracing_to_L_fun(1);//一直走到左转磁条
car_turn_L_fun();//左转90度
find_MAG_fun();//摆正车身
Tracing_to_L_fun(1);//走到左转位置
car_turn_L_fun();//左转90度
find_MAG_fun();//修正车身
Tracing_to_L_fun(3);//走到第三个左转磁条
car_turn_L_fun();//左转90度
find_MAG_fun();//修正车身
/*************************main.h*************************/
//小车走 托盘的4个距离  
# define TUOPAN_DIS_0		20				  //210
# define TUOPAN_DIS_1		1000				//1100
# define TUOPAN_DIS_2		1970				//1900
# define TUOPAN_DIS_3		2870				//2700




//小车走 集中站的4个距离
# define JIZHONGZHAN_DIS_0		0
# define JIZHONGZHAN_DIS_1		90
# define JIZHONGZHAN_DIS_2		1920
# define JIZHONGZHAN_DIS_3		3645
/*************************main.c*************************/
u32 tuopan_dis[4];								//托盘4个距离
u32 jizhongzhan_dis[4];						//集中站4个距离


void ram_init(void)//小车在转盘路 走的4个距离    集中站路 走的4个距离 赋值
{
	tuopan_dis[0] = TUOPAN_DIS_0;//
	tuopan_dis[1] = TUOPAN_DIS_1;//
	tuopan_dis[2] = TUOPAN_DIS_2;//
	tuopan_dis[3] = TUOPAN_DIS_3;//

	jizhongzhan_dis[0] = JIZHONGZHAN_DIS_0;//
	jizhongzhan_dis[1] = JIZHONGZHAN_DIS_1;//
	jizhongzhan_dis[2] = JIZHONGZHAN_DIS_2;//
	jizhongzhan_dis[3] = JIZHONGZHAN_DIS_3;//
}
//执行从托盘拿物块 送到集中站的全部过程
void Grab_garbage_fun(u8 tuopan,u8 jizhongzhan)//小车从起始点开始  抓取物块  走到集中站   tuopan是托盘编号  jizhongzhan 是集中站编号    走到起点
{
	//转盘路
	Tracing_to_cross_and_dis_fun(tuopan_dis[tuopan]);//走过十字磁条后再走一段距离    tuopan_dis[1]是第一个距离
	delay_ms(1000);
	arm_pick_up_block_fun();//机械臂抓物块 全部动作
	car_move_back_fun(500);//小车后退300个距离
	Tracing_to_L_fun(1);//走到第一个左转
	car_turn_L_fun();//自己左转90度
	find_MAG_fun();//小车车身修正磁条
	
	//垃圾桶路
	Tracing_to_L_fun(1);//一直走到左转磁条
	car_turn_L_fun();//左转90度
	find_MAG_fun();//摆正车身
	
	//集中站路
	if(jizhongzhan==0)//如果是集中站第一个门
	{
		car_move_back_fun(100);//小车后退100个距离
		
		send_cmd_to_Concentration_station_fun(DOOR_OPEN,jizhongzhan);//通过串口2给集中站发送开门命令 DOOR_OPEN是开门的命令 jizhongzhan是集中站第几个门
		jizhongzhan_fun();//机械臂扔集中站全部动作
		send_cmd_to_Concentration_station_fun(DOOR_CLOSE,jizhongzhan);//通过串口2发送关门命令
		
		car_move_forward_fun(100);//小车向前移动100个距离
		
		Tracing_to_L_fun(1);//小车走到左转位置
		car_turn_L_fun();//左转90度
		find_MAG_fun();//依据修正车身
	}
	if((jizhongzhan==1)||(jizhongzhan==2))//如果是集中站的2门和3门
	{
		Tracing_to_cross_and_dis_fun(jizhongzhan_dis[jizhongzhan]);//小车走过十字磁条后再走一段距离
		send_cmd_to_Concentration_station_fun(DOOR_OPEN,jizhongzhan);//发送开门命令
		jizhongzhan_fun();//机械臂扔集中站全部动作
		send_cmd_to_Concentration_station_fun(DOOR_CLOSE,jizhongzhan);//小车发送关门命令
		
		Tracing_to_L_fun(1);//走到左转位置
		car_turn_L_fun();//左转90度
		find_MAG_fun();//修正车身
	}
	if(jizhongzhan==3)//如果是第四个门
	{
		//Tracing_to_cross_and_dis_fun(jizhongzhan_dis[jizhongzhan]);//走过十字磁条后 再走一段距离
		Tracing_to_L_fun(1);//走到左转位置
		send_cmd_to_Concentration_station_fun(DOOR_OPEN,jizhongzhan);//发送开门命令
		jizhongzhan_fun();//扔集中站
		send_cmd_to_Concentration_station_fun(DOOR_CLOSE,jizhongzhan);//发送关门命令
		
		car_turn_L_fun();//左转90度
		find_MAG_fun();//修正车身
	}
	
	//车库路
	Tracing_to_L_fun(3);//走到第三个左转磁条
	car_turn_L_fun();//左转90度
	find_MAG_fun();//修正车身

}
/*************************car.c*************************/
void Tracing_to_cross_and_dis_fun(u32 dis)//寻迹走到十字后再走一定距离
{
    u8 i;

    MOTO_0_F;	//轮子方向控制(默认向前)
    MOTO_1_F;
    MOTO_2_F;
    MOTO_3_F;

    speed_PID_data_init();	//PID清0

    distance_now_u32 = 0;

    speed_set_mun_L = 0;
    speed_set_mun_R = 0;

    Tracing_dis_bit = 1;

    ok_bit = 1;

    shizi_bit = 0;

    //缓启动
    for(i = 0; i < 20; i++)
    {
        speed_set_mun_L++;
        speed_set_mun_R++;
        delay_ms(25);

        //if(chack_MAG_fun()==1)
        if(shizi_bit == 1)
        {
            ok_bit = 0;
            distance_now_u32 = 0;
        }
    }

    if(ok_bit == 1)
    {
        while(ok_bit)
        {
            //if(chack_MAG_fun()==1)
            if(shizi_bit == 1)
            {
                ok_bit = 0;
                distance_now_u32 = 0;
            }
        }
    }



    ok_bit = 1;
    while(ok_bit)
    {
        if(distance_now_u32 >= dis)
        {
            ok_bit = 0;
        }
    }


    speed_set_mun_L = 20;
    speed_set_mun_R = 20;

    for(i = 0; i < 20; i++)
    {
        speed_set_mun_L--;
        speed_set_mun_R--;
        delay_ms(50);
    }

    car_pwm_stop();
}
void speed_PID_data_init(void)//4个车轮PID参数 清零 在运行PID程序之前  调用
{
    u8 i;

    for(i = 0; i < 4; i++)
    {
        e[i] = 0;
        num[i] = 0;
        duk[i] = 0;
        e1[i] = 0;
        uk[i] = 0;
        uk1[i] = 0;
        out[i] = 0;
        e2[i] = 0;
        PWMTime[i] = 0;
    }
}
void timer_Tracing_fun(void)////寻迹走 定时器5中断函数中 运行
{
    u16 speed_Tracing_L;
    u16 speed_Tracing_R;


    MAG_dat = mag_test();//读磁条

    //printf("%d\r\n",MAG_dat);


    if(MAG_dat == 0)
    {
        speed_Tracing_L = speed_set_mun_L;
        speed_Tracing_R = speed_set_mun_R;
    }
    else if((MAG_dat > 0) && (MAG_dat <= 4)) //1到4之间  磁条在右边   小车偏左   左边加速
    {
        speed_Tracing_L = gain_fun(speed_set_mun_L, 0.2);
        speed_Tracing_R = gain_fun(speed_set_mun_R, -0.2);
    }
    else if((MAG_dat >= 5) && (MAG_dat <= 7)) //5到7之间  磁条在右边   小车偏左   左边加速
    {
        speed_Tracing_L = gain_fun(speed_set_mun_L, 0.4);
        speed_Tracing_R = gain_fun(speed_set_mun_R, -0.4);
    }
    else if((MAG_dat < 0) && (MAG_dat >= -4)) //-1到-4  磁条在左边   小车偏右   右边加速
    {
        speed_Tracing_L = gain_fun(speed_set_mun_L, -0.2);
        speed_Tracing_R = gain_fun(speed_set_mun_R, 0.2);
    }
    else if((MAG_dat <= -5) && (MAG_dat >= -7)) //-5到-7  磁条在左边   小车偏右   右边加速
    {
        speed_Tracing_L = gain_fun(speed_set_mun_L, -0.4);
        speed_Tracing_R = gain_fun(speed_set_mun_R, 0.4);
    }
    else if(MAG_dat == 200) //跑出磁条了
    {
        car_pwm_stop();//小车停止
        bell_fun(3);//蜂鸣器响3下
    }
    else
    {
        speed_Tracing_L = speed_set_mun_L;//设置左边轮子速度
        speed_Tracing_R = speed_set_mun_R;//设置右边轮子速度
    }

    timer5_speed_0_pid_fun(speed_Tracing_L);//设置4个轮字的PID转速
    timer5_speed_1_pid_fun(speed_Tracing_R);//
    timer5_speed_2_pid_fun(speed_Tracing_L);//
    timer5_speed_3_pid_fun(speed_Tracing_R);//
}

机械臂/舵机

管脚 对应 模式
PF^14 JXB01 输出,推挽,无上下拉
PE^8 JXB02 同上
PE^10 JXB11 同上
PF^13 JXB12 同上
PF^12 JXB21 同上
PF^11 JXB22 同上
PF^15 TJ1(机械臂复位) 输入,推挽,无上下拉
PG^0 TJ2(机械臂复位) 同上
PG^1 TJ3(机械臂复位) 同上
管脚 对应 频率
PB^0(TIM3_CH3) DJ 50Hz(20ms)
/*************************main.h*************************/
# define STEERING_ENGINE_OPEN		335			//爪子张开时舵机的参数				舵机张开不能太大  会卡住  卡住会发热
/*************************Mechanical_arm.h*************************/
# define JXB01_L		(GPIOF->BSRRH = GPIO_Pin_14)//小臂脉冲
# define JXB01_H		(GPIOF->BSRRL = GPIO_Pin_14)//
# define	JXB01_PP		GPIOF->ODR^=GPIO_Pin_14

# define JXB02_UP		(GPIOE->BSRRH = GPIO_Pin_8)//小臂上
# define JXB02_DN		(GPIOE->BSRRL = GPIO_Pin_8)//小臂下

# define JXB11_L		(GPIOE->BSRRH = GPIO_Pin_10)//底盘脉冲
# define JXB11_H		(GPIOE->BSRRL = GPIO_Pin_10)//

# define JXB12_R		(GPIOF->BSRRH = GPIO_Pin_13)//底盘右
# define JXB12_L		(GPIOF->BSRRL = GPIO_Pin_13)//底盘左

# define JXB21_L		(GPIOF->BSRRH = GPIO_Pin_12)//大臂脉冲
# define JXB21_H		(GPIOF->BSRRL = GPIO_Pin_12)//

# define JXB22_F		(GPIOF->BSRRH = GPIO_Pin_11)//大臂前
# define JXB22_B		(GPIOF->BSRRL = GPIO_Pin_11)//大臂后

//机械臂复位
# define JSB_W1_Input		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_15)//F15
# define JSB_W2_Input		GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_0)//G0
# define JSB_W3_Input		GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_1)//G1
/*************************Mechanical_arm.c*************************/
//机械臂全部端口初始化
void gpio_arm_init(void)
{
	//机械臂控制输出
	GPIO_init(GPIO_F,GPIO_Pin_11,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//F11
	GPIO_init(GPIO_F,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//F12
	GPIO_init(GPIO_F,GPIO_Pin_13,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//F13
	GPIO_init(GPIO_E,GPIO_Pin_10,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//E10
	GPIO_init(GPIO_E,GPIO_Pin_8,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//E8
	GPIO_init(GPIO_F,GPIO_Pin_14,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//F14
	
	//机械臂归位信号输入
	GPIO_init(GPIO_F,GPIO_Pin_15,GPIO_Mode_IN,GPIO_OType_PP,GPIO_PuPd_NOPULL);//F15
	GPIO_init(GPIO_G,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_PP,GPIO_PuPd_NOPULL);//G0
	GPIO_init(GPIO_G,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_PP,GPIO_PuPd_NOPULL);//G1
	
	
	TIM3_CH3_PWM_Init_PB0(5000-1,336-1);//夹子舵机初始化调用  50HZ  20MS
	
	steering_engine(STEERING_ENGINE_OPEN);	//夹子舵机 设置位置
}

//TIM3_CH3   PB0   PWM   舵机    0.5ms  2.5ms
//参数:5000-1,336-1
void TIM3_CH3_PWM_Init_PB0(u32 arr,u32 psc)//    小车机械臂  夹子的 舵机初始化  舵机是PWM控制的
{ 
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;	//定义结构体
	TIM_OCInitTypeDef TIM_OCInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);	//使能TIM3时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);	//使能B组时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;	//PB^0
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	//没上下拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度
	GPIO_Init(GPIOB,&GPIO_InitStructure);	//初始化
	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource0,GPIO_AF_TIM3);	//把管脚PB^0复用为TIM3
	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//分频因子
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
	TIM_TimeBaseStructure.TIM_Prescaler = psc;	//预分频值
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; 	//默认
	TIM_TimeBaseStructure.TIM_Period = arr;	//重装载值
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);	//初始化
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	//PWM1模式
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//开启OC输出到对应引脚
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;	//互补输出使能
	TIM_OCInitStructure.TIM_Pulse = (arr+1)/2;	//占空比
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;	//极性高
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;	//互补通道极性高
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;	//空闲状态输出高电平
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;	//互补通道的空闲时输出低电平
	
	TIM_OC3Init(TIM3,&TIM_OCInitStructure);	//初始化
	TIM_Cmd(TIM3,ENABLE);	//使能TIM3
	
	TIM_CtrlPWMOutputs(TIM3,ENABLE);	//可有可无
	
	//TIM_SetCompare3(TIM3,350);//620=2.5ms    125=0.5ms   370=1.5ms    620是夹住   125是放开
}
//夹子舵机 设置位置  
void steering_engine(u16 dat)
{
	TIM_SetCompare3(TIM3,dat);//620=2.5ms    125=0.5ms   370=1.5ms    620是夹住   125是放开
}
//机械臂 底盘 大臂 小臂  依次归位
void arm_position_init_all(void)
{
	arm0_position_init();
	delay_ms(500);
	arm1_position_init();
	delay_ms(500);
	arm2_position_init();
	delay_ms(500);
	
	//可以在这里添加下面的 机械臂控制函数  达到调整的目的
	arm_2_UP(90);//小臂向上
	arm_1_B(80);//大臂向后
	
	
	/*
	//下面是控制 大臂 和 小臂的 函数   参数是脉冲数  根据需要修改大小 20只是示意   需要哪个就复制哪个到上面
	arm_1_F(20);//大臂向前
	arm_1_B(20);//大臂向后
	arm_2_UP(20);//小臂向上
	arm_2_DN(20);//小臂向下
	*/
}
//机械臂大臂 归位
void arm1_position_init(void)
{
	u8 ok_bit=1;
	u8 read_bit;
	u16 count;
	
	count = 0;
	
	read_bit = JSB_W2_Input;
	
	if(read_bit==0)//在原位
	{
		//需要伸出  直到出去
		while(ok_bit)
		{
			JXB22_F;//向前
			JXB21_H;//给脉冲
			delay_ms(TIME_arm);
			JXB21_L;//给脉冲
			delay_ms(TIME_arm);
			
			read_bit = JSB_W2_Input;
			
			if(read_bit==1)
			{
				ok_bit = 0;
			}
			
			count++;
			if(count>=200)
			{
				ok_bit = 0;
				bell_fun(3);
			}
		}
		
		arm_1_F(20);
		
		ok_bit = 1;
		
		while(ok_bit)
		{
			JXB22_B;//向后
			JXB21_H;//给脉冲
			delay_ms(TIME_arm);
			JXB21_L;//给脉冲
			delay_ms(TIME_arm);
			
			read_bit = JSB_W2_Input;
			
			if(read_bit==0)
			{
				ok_bit = 0;
			}
		}
	}
	else
	{
		arm_1_F(120);
		
		read_bit = JSB_W2_Input;
		
		if(read_bit==0)//在原位
		{
			//需要伸出  直到出去
			while(ok_bit)
			{
				JXB22_F;//向前
				JXB21_H;//给脉冲
				delay_ms(TIME_arm);
				JXB21_L;//给脉冲
				delay_ms(TIME_arm);
				
				read_bit = JSB_W2_Input;
				
				if(read_bit==1)
				{
					ok_bit = 0;
				}
				
				count++;
				if(count>=200)
				{
					ok_bit = 0;
					bell_fun(3);
				}
			}
			
			arm_1_F(20);
			
			ok_bit = 1;
			
			while(ok_bit)
			{
				JXB22_B;//向后
				JXB21_H;//给脉冲
				delay_ms(TIME_arm);
				JXB21_L;//给脉冲
				delay_ms(TIME_arm);
				
				read_bit = JSB_W2_Input;
				
				if(read_bit==0)
				{
					ok_bit = 0;
				}
			}
		}
		else
		{
			while(ok_bit)
			{
				JXB22_B;//向后
				JXB21_H;//给脉冲
				delay_ms(TIME_arm);
				JXB21_L;//给脉冲
				delay_ms(TIME_arm);
				
				read_bit = JSB_W2_Input;
				
				if(read_bit==0)
				{
					ok_bit = 0;
				}
			}
		}
	}
	
	arm1_position_now = 0;
}



//机械臂小臂 归位
void arm2_position_init(void)
{
	u8 ok_bit=1;
	u8 read_bit;
	u16 count;
	
	read_bit = JSB_W3_Input;
	
	if(read_bit==0)//在原位
	{
		//需要伸出  直到出去
		while(ok_bit)
		{
			JXB02_UP;//向上
			JXB01_H;//给脉冲
			delay_ms(TIME_arm);
			JXB01_L;//给脉冲
			delay_ms(TIME_arm);
			
			read_bit = JSB_W3_Input;
			
			if(read_bit==1)
			{
				ok_bit = 0;
			}
			
			count++;
			if(count>=300)
			{
				ok_bit = 0;
				bell_fun(3);
			}
		}
		
		arm_2_UP(20);
		
		ok_bit = 1;
		
		while(ok_bit)
		{
			JXB02_DN;//向下
			JXB01_H;//给脉冲
			delay_ms(TIME_arm);
			JXB01_L;//给脉冲
			delay_ms(TIME_arm);
			
			read_bit = JSB_W3_Input;
			
			if(read_bit==0)
			{
				ok_bit = 0;
			}
		}
	}
	else
	{
		while(ok_bit)
		{
			JXB02_DN;//向下
			JXB01_H;//给脉冲
			delay_ms(TIME_arm);
			JXB01_L;//给脉冲
			delay_ms(TIME_arm);
			
			read_bit = JSB_W3_Input;
			
			if(read_bit==0)
			{
				ok_bit = 0;
			}
		}
	}
	
	arm2_position_now = 0;
}








//机械臂底座左右 归位
void arm0_position_init(void)
{
	u8 ok_bit=1;
	u8 read_bit;
	u8 read_bit_new;
	u8 read_bit_old;
	
	u16 recode_1 = 0;
	u16 recode_2 = 0;
	u16 middle;
	u16 i;
	
	read_bit = JSB_W1_Input;//转盘有洞的位置是1    没有洞的位置时0
	
	if(read_bit==1)//在中点
	{
		//需要伸出  直到出去
		while(ok_bit)
		{
			JXB12_L;//向左
			JXB11_H;//给脉冲
			delay_ms(TIME_arm);
			JXB11_L;//给脉冲
			delay_ms(TIME_arm);
			
			read_bit = JSB_W1_Input;
			
			if(read_bit==0)//到了原点边缘
			{
				ok_bit = 0;
			}
		}
		
		delay_ms(100);
		
		arm_0_R(26);//26个脉冲可以回到洞的 正中心  如果不是  就可以调26这个数据
	}
	else
	{
		arm_0_R(100);
	
		for(i=0;i<200;i++)
		{
			read_bit_new = JSB_W1_Input; 
			if((read_bit_new==1)&&(read_bit_old==0)) // 第一次在有洞的地方
			{
				recode_1 = i;
			}
			if((read_bit_new==0)&&(read_bit_old==1)) // 第一次在没有洞的地方
			{
				recode_2 = i;
			}
			
			read_bit_old = read_bit_new;
			JXB12_L;//向左
			JXB11_H;//给脉冲
			delay_ms(TIME_arm);
			JXB11_L;//给脉冲
			delay_ms(TIME_arm);
			
		}
		
		delay_ms(100);
		
		if((recode_1!=0)&&(recode_2!=0))
		{
			middle = (200 - recode_2) + (recode_2 - recode_1)/2;
		
			arm_0_R(middle);
		}
		else
		{
			bell_fun(3);
		}
		
		
		
		//再按照第一个步骤 重新校准一下
		while(ok_bit)
		{
			JXB12_L;//向左
			JXB11_H;//给脉冲
			delay_ms(TIME_arm);
			JXB11_L;//给脉冲
			delay_ms(TIME_arm);
				
			read_bit = JSB_W1_Input;
				
			if(read_bit==0)//到了原点边缘
			{
				ok_bit = 0;
			}
		}
			
		delay_ms(100);
			
		arm_0_R(26);//26个脉冲可以回到洞的 正中心  如果不是  就可以调26这个数据
	}
	
	arm0_position_now = 0;
}
//机械臂大臂  向后
void arm_1_B(u32 mun)
{
	u32 i;
	
	JXB22_B;
	
	for(i=0;i<mun;i++)
	{
		JXB21_H;//给脉冲
		delay_ms(TIME_arm);
		JXB21_L;
		delay_ms(TIME_arm);
	}
}




//小臂 向上
void arm_2_UP(u32 mun)
{
	u32 i;
	
	JXB02_UP;
	for(i=0;i<mun;i++)
	{
		JXB01_H;
		delay_ms(TIME_arm);
		JXB01_L;
		delay_ms(TIME_arm);
	}
}

蜂鸣器

管脚 对应 模式
PB^9 FMQ 输出,推挽,无上下拉
/*************************CAR.h*************************/
# define BELL_OFF	(GPIOB->BSRRH = GPIO_Pin_9)	//低电平不响
# define BELL_ON		(GPIOB->BSRRL = GPIO_Pin_9)	//高电平响(NPN管)
/*************************Car_init.c*************************/
//蜂鸣器端口 初始化
void gpio_bell_init(void)
{
    GPIO_init(GPIO_B, GPIO_Pin_9, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //B9

    BELL_ON;				//蜂鸣器响
    delay_ms(200);	//
    BELL_OFF;				//蜂鸣器灭
    delay_ms(500);	//
}

激光测距

管脚 对应 模式
PB^6 SDA 输出,开漏,无上下拉
PD^2 SCL 输出,推挽,无上下拉
/*************************Car_init.c*************************/
//激光测距端口初始化
void laser_gpio_init(void)
{
    GPIO_init(GPIO_B, GPIO_Pin_6, GPIO_Mode_OUT, GPIO_OType_OD, GPIO_PuPd_NOPULL); //B6		SDA
    GPIO_init(GPIO_D, GPIO_Pin_2, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //D2		SCL
}

串口2无线通信

/*************************STM32F40x_Usart_eval.c*************************/
void uart2_race_chack(void)//检测串口2 是否收到一帧数据
{
    if(usart2_race_over_bit)
    {
        LCD_ShowString_fun("433race");//屏幕显示

        if(usart2_race_buf[0] == 1)	//判断是否是小车ID
            LCD_ShowString_fun("ID=1");
        if(usart2_race_buf[0] == 2)
            LCD_ShowString_fun("ID=2");
        if(usart2_race_buf[0] == 3)
            LCD_ShowString_fun("ID=3");


        copy_fun(usart2_race_buf, cmd_race_buf, usart2_race_count); //拷贝usart2_race_buf 串口2接收数组 到 cmd_race_buf     usart2_race_count是拷贝字节数
        usart2_race_over_bit = 0;//收到一帧数据标志清零
        usart2_race_count = 0;//接收字节数清零

        delay_ms(1000);//转盘发命令会响一声  这里也响一声  延时半秒  能听到发送的嘀和 小车接收的嘀  便于判断  不延时 两个声音会混在一起

        bell_fun(1);//蜂鸣器响一声

        cmd_explan_fun();//命令解释函数  判断是不是自己的ID  提取里面的参数
    }
}
void copy_fun(u8 *p_in, u8 *p_out, u8 mun) //拷贝一个数组 到 另一个数组
{
    u8 i;
    for(i = 0; i < mun; i++)
    {
        *(p_out + i) = *(p_in + i);
    }
}
/*************************main.c*************************/
/*
第一字节是 ID  			小车的ID是2
第二字节是命令类型   小车拿送物块  1:轮盘-->集中站
第三字节是托盘号  		0-3
第四字节是集中站号 	0-3
*/

void cmd_explan_fun(void)//无线模块串口2发过来的数据  解析
{
	if(cmd_race_buf[0]==ID)//如果是自己的ID
	{
		switch(cmd_race_buf[1])//判断第二个字节
		{
			case 1://转盘到集中站命令
				tuopan_number = cmd_race_buf[2];		//托盘编号      第三字节
				lajitong_number = cmd_race_buf[3];	//集中站编号			第四字节
			
				//判断托盘号然后显示在屏上
				if(tuopan_number==0)
					LCD_ShowString_fun("tuopan=0");
				if(tuopan_number==1)
					LCD_ShowString_fun("tuopan=1");
				if(tuopan_number==2)
					LCD_ShowString_fun("tuopan=2");
				if(tuopan_number==3)
					LCD_ShowString_fun("tuopan=3");
				
				//判断集中站号然后显示在屏上
				if(lajitong_number==0)
					LCD_ShowString_fun("jizhongzhan=0");
				if(lajitong_number==1)
					LCD_ShowString_fun("jizhongzhan=1");
				if(lajitong_number==2)
					LCD_ShowString_fun("jizhongzhan=2");
				if(lajitong_number==3)
					LCD_ShowString_fun("jizhongzhan=3");
			
				//小车开始执行命令
				Grab_garbage_fun(tuopan_number,lajitong_number);	//执行从托盘拿物块 送到集中站的全部过程
			
				send_to_zhuanpan_cmd_fun();//给转盘发送一个数据  让转盘知道 小车的任务已经完成  转盘就可以再发送下一个任务
				delay_ms(2000);//延时
				send_to_zhuanpan_cmd_fun();
				//delay_ms(1000);//延时
				//send_to_zhuanpan_cmd_fun();
				break;
		}
	}
}

代码修改/添加记录

11/15

  • TIM5定时20ms改成别的计算方式
/*************************STM32F40x_Timer_eval.c*************************/
//定时器5初始化 定时20ms
//参数:10000, 168 计算公式:10000*168/84000000 = 0.02s = 20ms
void Timer5_Config(uint16_t period, uint16_t prescaler)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;	//定义结构体
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);	//使能TIM5时钟

    TIM_TimeBaseStructure.TIM_Period = period - 1;	//重装载值
    TIM_TimeBaseStructure.TIM_Prescaler = prescaler;	//预分频系数
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;	//分频因子
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);	//初始化

    TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE);	//TIM5中断使能
    TIM_Cmd(TIM5, ENABLE);	//使能TIM5

    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;	//中断通道为TIM5
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;	//抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;	//子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//使能通道
    NVIC_Init(&NVIC_InitStructure);	//中断初始化
}

11/16

  • 修改了宏定义 TUOPAN_DIS_3 的值,原本是2870
# define TUOPAN_DIS_3		2670				//2700第4个托盘的距离(已弃用)
# define TUOPAN_DIS_3		2900				//2022.11.17
  • 修改了 find_MAG_fun 函数
//找磁条
void find_MAG_fun(void)// 小车左转后  可能磁条不在中间  这个函数是让小车磁条 修正到中间
{
    u8 arr[10];
    speed_set_mun_L = 0;//设置左右速度为0
    speed_set_mun_R = 0;
    ok_bit = 1;


    while(ok_bit)
    {
        MAG_dat = mag_test();//读磁条
        sprintf((char *)arr, "num:%d", MAG_dat);
        LCD_ShowString_fun((char *)arr);
        speed_PID_bit = 1;//开始执行PID函数

        if(speed_set_mun_L < 10) //缓启动  限制速度在10以内
        {
            speed_set_mun_L++;
            speed_set_mun_R++;
        }

        if((MAG_dat >= 0 && MAG_dat <= 5) || (MAG_dat <= -4 && MAG_dat >= -1) ) //这个范围就不用管
        {
            ok_bit = 0;// 表示清零
            delay_ms(100);
            car_pwm_stop();//小车停止
        }
        else if(MAG_dat >= -5) //如果小车偏左或者右  就是设置车轮的方向   达到车身修正的目的
        {
            MOTO_0_B;
            MOTO_1_F;
            MOTO_2_B;
            MOTO_3_F;
        }
        else if(MAG_dat > 5) //如果小车偏左或者右  就是设置车轮的方向   达到车身修正的目的
        {
            MOTO_0_F;
            MOTO_1_B;
            MOTO_2_F;
            MOTO_3_B;
        }
        delay_ms(50);
    }
}

11/17

  • 修改了 arm_pick_up_block_fun 函数
void arm_pick_up_block_fun(void)//机械臂 在 托盘处  向右抓取全部动作
{
	
	arm_0_R(490);//右转490脉冲(原本500)
	delay_ms(500);
	
	
	arm_1_F(100);//大臂向前
	delay_ms(500);
	
	arm_2_DN(70);//小臂向下
	delay_ms(500);
	
	arm_1_F(115);//大臂向前
	delay_ms(500);
	
	arm_2_DN(30);//小臂向下(原本55)	这个太大会导致扫描前碰到物品所以调小点
	delay_ms(500);
	
	acoustic_wave_find_box_fun();//用激光找找盒子动作
	delay_ms(500);
	//添加1
	arm_0_R(4);	//底盘右转
	delay_ms(500);
	
	arm_2_DN(60);//小臂向下
	delay_ms(500);
	//添加2
	arm_1_F(5);	//大臂向前
	arm_2_UP(10);	//小臂抬起
	
	arm3_clip_fun();//夹子夹物块
	delay_ms(500);
	
	arm_2_UP(150);//小臂抬起
	delay_ms(500);
	
	arm_0_L(500);//底盘向左500
	delay_ms(500);
	
	arm_position_init_all();//三臂归位
}
  • uart2_race_chack 函数里添加
void uart2_race_chack(void)//检测串口2 是否收到一帧数据
{
    if(usart2_race_over_bit)
    {
        LCD_ShowString_fun("433race");//屏幕显示
		if(usart2_race_buf[0]==1)
			LCD_ShowString_fun("ID=1");
		if(usart2_race_buf[0]==1)
			LCD_ShowString_fun("ID=2");
		if(usart2_race_buf[0]==1)
			LCD_ShowString_fun("ID=3");


        copy_fun(usart2_race_buf, cmd_race_buf, usart2_race_count); //拷贝usart2_race_buf 串口2接收数组 到 cmd_race_buf     usart2_race_count是拷贝字节数
        usart2_race_over_bit = 0;//收到一帧数据标志清零
        usart2_race_count = 0;//接收字节数清零
		uart2_buf_init();	//添加这个清空数组
        delay_ms(1000);//转盘发命令会响一声  这里也响一声  延时半秒  能听到发送的嘀和 小车接收的嘀  便于判断  不延时 两个声音会混在一起

        bell_fun(1);//蜂鸣器响一声

        cmd_explan_fun();//命令解释函数  判断是不是自己的ID  提取里面的参数
    }
}
  • cmd_explan_fun 函数里的两行屏蔽掉,只留下一行(这个bug搞了我几个小时一直只能接收一次任务…),这样就可以接收完一次任务回到原点又接收下一次任务了
send_to_zhuanpan_cmd_fun();
//delay_ms(2000);//延时
//send_to_zhuanpan_cmd_fun();

串口测试函数

  • 此函数是拿来测试底盘和机械臂的方便调参 ``
void uart1_race_chack(void)//检查串口1 是否接收完毕
{
    /*底盘调试*/
    if(usart1_race_over_bit)
    {
        usart1_race_over_bit = 0;


        if(usart1_race_buf[0] == '0')
        {
            bell_fun(1);
            arm_0_L(maichong);//左转
            printf("左转底盘:%d@@\r\n", maichong);
        }
        if(usart1_race_buf[0] == '1')		//执行步骤1底盘
        {
            bell_fun(1);
            arm_0_R(maichong);//右转
            printf("右转底盘:%d@@\r\n", maichong);
        }
        if(usart1_race_buf[0] == '+')	//脉冲+
        {
            bell_fun(1);
            maichong += 1;
            printf("底盘脉冲加:%d@@\r\n", maichong);
        }
        if(usart1_race_buf[0] == '-')	//脉冲-
        {
            bell_fun(1);
            maichong -= 1;
            printf("底盘脉冲减:%d@@\r\n", maichong);
        }


        /*第一次大臂调试*/
        if((usart1_race_buf[0] == 'x') && (usart1_race_buf[1] == '1')) //大臂前		//执行步骤2
        {
            bell_fun(1);
            arm_1_F(x1);
            printf("第一次大臂前:%d@@\r\n", x1);
        }
        if((usart1_race_buf[0] == 'x') && (usart1_race_buf[1] == '0')) //大臂后
        {
            bell_fun(1);
            arm_1_B(x1);
            printf("第一次大臂后:%d@@\r\n", x1);
        }
        if((usart1_race_buf[0] == 'x') && (usart1_race_buf[1] == '+')) //脉冲+
        {
            bell_fun(1);
            x1 += 1;
            printf("第一次大臂加:%d@@\r\n", x1);
        }
        if((usart1_race_buf[0] == 'x') && (usart1_race_buf[1] == '-')) //脉冲-
        {
            bell_fun(1);
            x1 -= 1;
            printf("第一次大臂减:%d@@\r\n", x1);
        }


        /*第一次小臂调试*/
        if((usart1_race_buf[0] == 'd') && (usart1_race_buf[1] == '1')) //小臂前		//执行步骤3
        {
            bell_fun(1);
            arm_2_DN(d1);
            printf("第一次小臂前:%d@@\r\n", d1);
        }
        if((usart1_race_buf[0] == 'd') && (usart1_race_buf[1] == '0')) //小臂后
        {
            bell_fun(1);
            arm_2_UP(d1);
            printf("第一次小臂后:%d@@\r\n", d1);
        }
        if((usart1_race_buf[0] == 'd') && (usart1_race_buf[1] == '+')) //脉冲+
        {
            bell_fun(1);
            d1 += 1;
            printf("第一次小臂加:%d@@\r\n", d1);
        }
        if((usart1_race_buf[0] == 'd') && (usart1_race_buf[1] == '-')) //脉冲-
        {
            bell_fun(1);
            d1 -= 1;
            printf("第一次小臂减:%d@@\r\n", a1);
        }

        /*第二次小臂调试*/
        if((usart1_race_buf[0] == 'a') && (usart1_race_buf[1] == '1')) //小臂前		//执行步骤4
        {
            bell_fun(1);
            arm_1_F(a1);
            printf("第二次小臂前:%d@@\r\n", a1);
        }
        if((usart1_race_buf[0] == 'a') && (usart1_race_buf[1] == '0')) //小臂后
        {
            bell_fun(1);
            arm_1_B(a1);
            printf("第二次小臂后:%d@@\r\n", a1);
        }
        if((usart1_race_buf[0] == 'a') && (usart1_race_buf[1] == '+')) //脉冲+
        {
            bell_fun(1);
            a1 += 1;
            printf("第二次小臂加:%d@@\r\n", a1);
        }
        if((usart1_race_buf[0] == 'a') && (usart1_race_buf[1] == '-')) //脉冲-
        {
            bell_fun(1);
            a1 -= 1;
            printf("第二次小臂减:%d@@\r\n", a1);
        }


        /*第二次大臂调试*/
        if((usart1_race_buf[0] == 'b') && (usart1_race_buf[1] == '1')) //大臂前		//执行步骤5
        {
            bell_fun(1);
            arm_2_DN(b1);
            printf("第二次大臂前:%d@@\r\n", b1);
        }
        if((usart1_race_buf[0] == 'b') && (usart1_race_buf[1] == '0')) //大臂后
        {
            bell_fun(1);
            arm_2_UP(b1);
            printf("第二次大臂后:%d@@\r\n", b1);
        }
        if((usart1_race_buf[0] == 'b') && (usart1_race_buf[1] == '+')) //脉冲+
        {
            bell_fun(1);
            b1 += 1;
            printf("第二次大臂加:%d@@\r\n", b1);
        }
        if((usart1_race_buf[0] == 'b') && (usart1_race_buf[1] == '-')) //脉冲-
        {
            bell_fun(1);
            b1 -= 1;
            printf("第二次大臂减:%d@@\r\n", b1);
        }


        uart1_buf_init();	//清空数组
    }
}

<2>集中站

问题
组成 口袋机,集中站控制板,LED点阵屏(可以用于显示信息),有害垃圾箱,其他垃圾箱,厨余垃圾箱,可回收垃圾箱;4个舵机输出接口,分别控制4个箱门
集中站功能 接收小车或者其他单元发过来的命令,开启或者关闭箱门
接收其他单元发过来的命令,开启或者关闭LED灯条
接收控制转盘的口袋机发送过来的文字显示在LED点阵屏上
点阵 GT30L32S4W为字库芯片,通过SPI接口与口袋机通讯,可实现读取字库数据,显示在LED点阵屏上
LED 每个垃圾箱都安装了SW2812灯条,可以独立控制每个灯条的颜色

代码修改/添加记录

集中站代码详解(主要部分)

  • 首先把管脚 D7 初始化(没有用到所以要输出高电平,防止SRAM干扰其他端口)

  • 定时器1定时功能代码(暂没用上)

//定时器1初始化
void Timer1_Config(uint16_t period, uint16_t prescaler)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;	//定义结构体
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);	//使能TIM5时钟
    TIM_TimeBaseStructure.TIM_Period = period - 1;	//自动重装载值(溢出值)
    TIM_TimeBaseStructure.TIM_Prescaler = prescaler - 1;	//预分频系数
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;	//分频因子,不分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);	//初始化

    NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;	//定时器1通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//使能通道
    NVIC_Init(&NVIC_InitStructure);	//中断初始化
	
	TIM_ClearITPendingBit(TIM1,TIM_IT_Update);//清空中断标志位
    TIM_ITConfig(TIM1, TIM_IT_Update|TIM_IT_Trigger, ENABLE);	//定时器中断源为 更新中断源/触发中断源
    TIM_Cmd(TIM1, ENABLE);	//使能定时器1
}

void TIM1_UP_TIM10_IRQHandler(void)
{
	static uint16_t count = 0;
	if(TIM_GetITStatus(TIM1,TIM_IT_Update) != RESET)
	{
		count++;
		if(count >= 20)
		{		
            //执行动作
		}
		TIM_ClearITPendingBit(TIM1,TIM_IT_Update);//清空中断
	}
}

Timer1_Config(1000,168);	//定时1ms

舵机

  • 4个舵机(顺序是从有害垃圾那开始1—2—3—4)
  • 管脚定义等跟转盘一样

串口2无线通信

  • 管脚定义等跟转盘一样

灯条

  • SPI1引脚
管脚 对应 模式
PB^3 LCD_CLK 复用,推挽,无上下拉
PB^4 LCD_MISO(输入) 同上
PB^5 LCD_MOSI(输出) 同上
  • 灯的控制引脚
管脚 对应 模式 对应集中站
PD^4 2812_EN1 输出,推挽,无上下拉 有害垃圾
PA^8 2812_EN2 同上 其他垃圾
PE^7 2812_EN3 同上 厨余垃圾
PE^0 2812_EN4 同上 可回收物
  • WS2812芯片

高位在前低位在后(和一般十六进制颜色码相反)

正常十六进制(16位) 此程序里十六进制(32位) 表示颜色
0xFFFFFF 0xFFFFFFFF 白色
0x000000 0x00000000 全灭
0xFF0000 0x000000FF 红色
0x00FF00 0x0000FF00 绿色
0x0000FF 0x00FF0000 蓝色
0xFFFF00 0x0000FFFF 黄色
0xCE09FB 0x00BF90EC 紫色
0xFB0083 0x003800BF 粉红色
  • 74HC32芯片:四组2输入端
/*************************STM32F40x_GPIO_Init.c*************************/
# define WS2812_EN1_L	(GPIOD->BSRRH = GPIO_Pin_4)
# define WS2812_EN1_H	(GPIOD->BSRRL = GPIO_Pin_4)	//

# define WS2812_EN2_L	(GPIOA->BSRRH = GPIO_Pin_8)
# define WS2812_EN2_H	(GPIOA->BSRRL = GPIO_Pin_8)

# define WS2812_EN3_L	(GPIOE->BSRRH = GPIO_Pin_7)
# define WS2812_EN3_H	(GPIOE->BSRRL = GPIO_Pin_7)

# define WS2812_EN4_L	(GPIOE->BSRRH = GPIO_Pin_0)
# define WS2812_EN4_H	(GPIOE->BSRRL = GPIO_Pin_0)
/*************************WS2812.c*************************/
//灯带使用的是SPI的 mosi 这个端口  所以要初始化SPI1
void SPI1_WS2812_Init(void)
{

    SPI_InitTypeDef  SPI_InitStructure;	//定义结构体
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);	//使能对应时钟

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);	//将管脚PB^3复用为SPI1
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1);	//将管脚PB^4复用为SPI1
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);	//将管脚PB^5复用为SPI1

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推挽
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;	//无上下拉

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;	//PB^3
    GPIO_Init(GPIOB, &GPIO_InitStructure);	//初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;	//PB^4
    GPIO_Init(GPIOB, &GPIO_InitStructure);	//初始化

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;	//PB^5
    GPIO_Init(GPIOB, &GPIO_InitStructure);	//初始化

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);	//使能SPI时钟

    SPI_I2S_DeInit(SPI1);	//将外设SPI1寄存器重设为缺省值

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	//设置为主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	//SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;	//时钟悬空低
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//数据捕获于第一个时钟沿
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;	//内部NSS信号有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;	//波特率预分频值为16
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//数据传输从MSB位开始(高位)
    SPI_InitStructure.SPI_CRCPolynomial = 7;	//不使用校验
    SPI_Init(SPI1, &SPI_InitStructure);	//初始化

    SPI_Cmd(SPI1, ENABLE);//使能SPI1
}
//灯带用到了门电路  有4个控制端口
void SW2812_init_fun(void)
{
    SPI1_WS2812_Init();

    GPIO_init(GPIO_D, GPIO_Pin_4, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //EN1
    GPIO_init(GPIO_A, GPIO_Pin_8, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //EN2
    GPIO_init(GPIO_E, GPIO_Pin_7, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //EN3
    GPIO_init(GPIO_E, GPIO_Pin_0, GPIO_Mode_OUT, GPIO_OType_PP, GPIO_PuPd_NOPULL); //EN4

    WS2812_EN1_H;
    WS2812_EN2_H;
    WS2812_EN3_H;
    WS2812_EN4_H;

    light_change_color_1(0x00000000);
    light_change_color_2(0x00000000);
    light_change_color_3(0x00000000);
    light_change_color_4(0x00000000);

}
/*************************main.c*************************/
//设置颜色
light_change_color_1(0x000000FF);//第一个灯红
light_change_color_2(0x00FFFFFF);//第二个灯白
light_change_color_3(0x0000FF00);//第三个灯绿
light_change_color_4(0x00FF0000);//第四个灯蓝

点阵

  • 字库芯片GT30L32S4W
管脚 对应 模式
PG^1 SIP2 SCK 输出,推挽,无上下拉
PE^13 SIP2 NSS 同上
PE^15 SIP2 MOSI 同上
PE^14 SIP2 MISO 输入,开漏,上拉
  • LED点阵屏
管脚 对应 模式
PG^9 LA 输出,推挽,无上下拉
PG^15 LB 同上
PD^2 LC 同上
PG^13 LD 同上
PD^1 CLK 同上
PA^15 LAT 同上
PC^11 R1 同上
PC^10 R2 同上
PG^12 G1 同上
PB^15 G2 同上
PC^12 EN 同上
  • 点阵是通过 74hc595 芯片控制行列
/*************************LED8080.c*************************/
//字库芯片的GPIO初始化
void GT30L32S4W_init(void)
{
	
/*
SIP2 SCK		G1
SIP2 MOSI		E15
SIP2 MISO		E14
SIP2 NSS		E13
*/
	
	GPIO_init(GPIO_G,GPIO_Pin_1,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//SCK
	GPIO_init(GPIO_E,GPIO_Pin_15,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//MOSI
	GPIO_init(GPIO_E,GPIO_Pin_14,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_UP);//MISO
	GPIO_init(GPIO_E,GPIO_Pin_13,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//NSS
	
	SPI_SCK_H;//SCK 输出高
	SPI_NSS_H;//NSS 输出高
}



//LED点阵屏的端口初始化
void LED8080_gpio_init(void)
{
/*
LA	G9
LB	G15
LC	D2
LD	G13
CLK	D1
LAT	A15
R1	C11
R2	C10
G1	G12
G2	B15
EN	C12
*/
	
	GPIO_init(GPIO_G,GPIO_Pin_9,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//LA
	GPIO_init(GPIO_G,GPIO_Pin_15,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//LB
	GPIO_init(GPIO_D,GPIO_Pin_2,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//LC
	GPIO_init(GPIO_G,GPIO_Pin_13,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//LD
	GPIO_init(GPIO_D,GPIO_Pin_1,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//CLK
	GPIO_init(GPIO_A,GPIO_Pin_15,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//LAT
	GPIO_init(GPIO_C,GPIO_Pin_11,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//R1
	GPIO_init(GPIO_C,GPIO_Pin_10,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//R2
	GPIO_init(GPIO_G,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//G1
	GPIO_init(GPIO_B,GPIO_Pin_15,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//G2
	GPIO_init(GPIO_C,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//EN
	
	EN_L;
	LA_H;
	LB_H;
	LC_H;
	LD_H;
}

串口2无线通信

  • 管脚定义等跟转盘一样
/*************************main.h*************************/
# define ID						0X03	//集中站的ID
/*************************STM32F40x_Usart_eval.c*************************/
u8 usart2_send_buf[100];	//串口2发送数据数组


# define UART2_RACE_MUN	200	//定义接收的命令最大不能超过这个数

u8 usart2_race_buf[UART2_RACE_MUN];	//接收数组
u8 usart2_race_over_bit;	//接收完成标志位
u8 usart2_race_count;	//接收的命令长度(如果超过UART2_RACE_MUN变回0)


//命令分析
void cmd_explan(void)
{
	u8 door_mun;
	u8 cmd_data;
	
	if(usart2_race_buf[0]==ID)	//判断是不是自己的ID
	{
		LCD_ShowString_fun("ID=3");	//显示自己的ID
		cmd_data = usart2_race_buf[1];
		door_mun = usart2_race_buf[2];
		
		if(cmd_data==0x01)
			LCD_ShowString_fun("kaimen");	//开门
		if(cmd_data==0x02)
			LCD_ShowString_fun("guanmen");	//关门
		
		if(door_mun==0)
			LCD_ShowString_fun("0");
		if(door_mun==1)
			LCD_ShowString_fun("1");
		if(door_mun==2)
			LCD_ShowString_fun("2");
		if(door_mun==3)
			LCD_ShowString_fun("3");
			
		steering_engine_ctl(cmd_data,door_mun);//开门或者关门函数  data 1是开门  2是关门    door_mun是门编号 0-3
	}
}

//判断是否接收到命令
void uart2_race_chack(void)
{
	if(usart2_race_over_bit)
	{
		usart2_race_over_bit= 0;
		
		Uart2_send_data(usart2_race_buf,usart2_race_count);
		
		usart2_race_count = 0;
		
		LCD_ShowString_fun("433_race");//屏上显示收到数据  但是不一定是给自己的
		
		cmd_explan();	//命令分析
	}
}
/*************************MOTO.c*************************/
//舵机控制      dat  1是开  2是关   mun是 门编号
void steering_engine_ctl(u8 cmd,u8 mun)
{
	switch(mun)
	{
		case 0:
			if(cmd==1)
				steering_engine_1_open();
			if(cmd==2)
				steering_engine_1_close();
			break;
		case 1:
			if(cmd==1)
				steering_engine_2_open();
			if(cmd==2)
				steering_engine_2_close();
			break;
		case 2:
			if(cmd==1)
				steering_engine_3_open();
			if(cmd==2)
				steering_engine_3_close();
			break;
		case 3:
			if(cmd==1)
				steering_engine_4_open();
			if(cmd==2)
				steering_engine_4_close();
			break;
	}
}

<3>转盘

问题
组成 AI智能图像识别部分(英伟达NANO主机,工业摄像头)
转盘(减速电机,光电码盘)
4个舵机
4个光电反射传感器
2个红外对射传感器
转盘控制板
口袋机

整个一个单独步骤运行流程

① 将沙盘上的所有单元上电,NANO主机开机并运行垃圾识别软件,在屏幕上能都看到图像,并 点击识别按钮

② 将要识别的模拟垃圾物块放置到转盘上(一般为1-4个),RFID刷卡(转盘控制板上有RFID读卡模块),启动转盘开始转动,当物块经过第一红外对射光电管后,口袋机知道物块已经进入识别区域,等待NANO主机通过MQTT通讯将垃圾分类的结果(0是可回收垃圾,1是有害垃圾,2是厨余垃圾,3是其他垃圾) 发送过来,当物块经过第二个红外对射光电管后,口袋机知道物块已经通过识别区域,将这个物块的类别和位置计数清零 (通过编码器可以知道物块走了多少个脉冲,第二个红外对射管是物块移动多少脉冲的记录的起始位置)

③ 口袋机得到NANO发来的0-3的数据后,当垃圾走到对应的托盘(0-3),口袋机控制舵机将垃圾推入托盘

④ 推入托盘后,口袋机通过无线模块(转盘控制板上),发送命令给小车(夹几号托盘的物块扔到几号集中站),小车收到命令后从起始位置出发,经过十字磁条,开始计数 (走多少距离到一号托盘),停下,夹物块

⑤ 托盘的下面有反射光电开关,当小车拿走物块后,转盘的口袋机知道小车已经把物块夹走了

⑥ 小车夹到物块后,通过磁条寻迹走到指定的集中站(转盘发给小车的命令中有),小车发送命令给集中站(开几号门),集中站通过舵机控制几号门开门

⑦ 小车执行扔垃圾程序,将物块扔进集中站,给集中站发送关门命令

⑧ 小车回到起始位置,给转盘发送命令(动作全部完成,集中站收到命令后,可以再给小车分配新的任务)

转盘上有 3个光电对射管 ,用于检测垃圾的通过, 1和2是用来检测垃圾在1和2之间的时候是否收到摄像发过来的分类数据(通过网线,使用MQTT协议), 3号光电对射是用来记录垃圾走到这个位置后开始编码器计数(转盘的电机上装有光电编码器,通过程序可以知道每个垃圾经过光电对射管3后走了多少距离),垃圾到达对应的托盘后,通过舵机,将垃圾推到托盘上

假设任务

小车收到转盘发过来的命令(例如:将1号托盘的垃圾运送到1号垃圾桶)

小车开始寻迹向前走,走过一个横着的十字磁条后,再走若干的脉冲,达到1号托盘;拿到垃圾后,小车继续寻迹向前,直到遇到左转磁条后停止,然后小车原地左转90度;直走若干脉冲直到遇到左转磁条,然后小车原地左转90度;将垃圾扔到1号集中站中;继续走到左转磁条,然后小车原地左转90度;连续通过3个左转磁条后停止;小车原地左转90度,小车回到起始位置。

沙盘各个单元之间的通讯

  • 图像识别的NANO主机与控制转盘的口袋机通过 MQTT协议 通讯,NANO通过网线连接路由器,转盘口袋机通过网线连接路由器,NANO运行图像识别分类程序后,建立了一个MQTT服务器,转盘口袋机作为客户端,订阅特定的主题,NANO每识别完一次垃圾后都会将分类结果向转盘口袋机发送一个消息
  • 转盘,集中站,小车,垃圾桶,每个单元都有一个 无线433模块,分别在各自的控制板上,并有天线,无线模块采用 透传广播 的方式传输数据(透传广播意思是一个口袋机的串口发送数据给模块,所有上电的模块都能收到,并通过串口发送给口袋机)
  • 口袋机和无线模块是通过 串口 收发数据的,波特率 115200
  • 如何做到单独通讯?

设置ID 的方式(传输数据的第一个字节是ID)

单元 ID
转盘 1
小车 2
集中站 3
垃圾桶1 4
垃圾桶2 5
垃圾桶3 6
垃圾桶4 7

举例:转盘给小车发送命令(串口发送的数据)

这个命令其他的单元也会受到,通过编写命令判断程序,可以判断第一字节是否是自己的ID, 是就执行命令,不是就不执行命令

字节1 字节2 字节3 字节4
2 (小车的ID) 要执行的命令 参数1 参数2
  • 口袋机给无线模块发送的数据要求

① 波特率115200

② 数据长度可变(最大64字节)

③ 一条命令的全部数据必须是连续的(模块会判断一帧连续的数据,如果数据不连续了,就认为一帧完成,打包通过无线发送出去),所以给无线模块发送数据时,要一帧全部发完,不要分开发

启动转盘步骤

打开沙盘电脑(密码:123456) — 右上角设置 — 系统设置 — 网络 — 点击左边那几个有线,看看哪个有显示IP地址的

下载程序到口袋机,然后打开桌面的 垃圾分拣 — 打开摄像头 — 开始识别

代码修改/添加记录

11/12

修改的文件 修改行号开始 修改了某处
main.c
STM32F40x_Timer_eval.c
330
22
Timer5_Config(74, 5);改成Timer5_Config(20000, 84);
把溢出值/预分频值计算公式改变

11/15

测试轮盘发命令给集中站开门关门–成功

void send_to_jzz_cmd(uint8_t oc)
{
	u8 cmd_buf[3];
	
	cmd_buf[0] = 3;//发送集中站的ID
	cmd_buf[1] = oc;	//开门
	cmd_buf[2] = 0;	//几号门
	
	Uart2_send_data(cmd_buf,3);//发送函数
	
	bell_fun(1);	//蜂鸣器响
}
//在按键里执行即可

轮盘代码详解(主要部分)

  • 清除数组大小
//数组初始化为0([x][0]:存储垃圾状态,[x][1]:垃圾类型,[x][2]转盘计数,[x][3]:暂没用到)
void task_buf_init(void)
{
	u8 i,j;
	for(i=0;i<TASK_MUN;i++)
	{
		for(j=0;j<4;j++)
		{
			task_buf[i][j] = 0;
		}
	}
}
  • 按键执行的内容
//判断哪个按键按下
u8 button_scan_fun(void)
{
	u8 i;
	u8 temp[8];
	u8 return_data;
	
	# define BUTTON_A  		4
	# define BUTTON_B  		3
	# define BUTTON_C  		2
	# define BUTTON_D  		1
	# define BUTTON_UP  		7
	# define BUTTON_DOWN  	6
	# define BUTTON_LEFT  	8
	# define BUTTON_RIGHT  5
	
	
	return_data = 0;
	
	temp[0] = READ_BUTTON1;	//D
	temp[1] = READ_BUTTON2;	//C	
	temp[2] = READ_BUTTON3;	//B
	temp[3] = READ_BUTTON4;	//A
	temp[4] = READ_BUTTON5;	//右
	temp[5] = READ_BUTTON6;	//下
	temp[6] = READ_BUTTON7;	//上
	temp[7] = READ_BUTTON8;	//左
	
	for(i=0;i<8;i++)
	{
		if(temp[i]==0)	//按下是低电平
		{
			return_data = i+1;	//返回对应的按键编码
			break;
		}
	}
	
	return return_data;	//没有按键按下则返回0
}
//根据返回的按键编码执行对应动作
void test_fun(void)
{
	u8 button_dat;
	
	button_dat = button_scan_fun();	//接收按键编码
	
	switch(button_dat)
	{
		case BUTTON_A:
			bell_fun(1);	//响一下
			steering_engine_1_push();	//舵机1推
			delay_ms(1000);	//延时1s
			steering_engine_1_shrink();	//舵机1收
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
		
		case BUTTON_B:
			bell_fun(1);	//响一下
			steering_engine_2_push();	//舵机2推
			delay_ms(1000);	//延时1s
			steering_engine_2_shrink();	//舵机2收
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
		
		case BUTTON_C:
			bell_fun(1);	//响一下
			steering_engine_3_push();	//舵机3推
			delay_ms(1000);	//延时1s
			steering_engine_3_shrink();	//舵机3收
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
		
		case BUTTON_D:
			bell_fun(1);	//响一下
			steering_engine_4_push();	//舵机4推
			delay_ms(1000);
			steering_engine_4_shrink();	//舵机4收
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
		
		case BUTTON_UP:
			bell_fun(1);	//响一下
			//开始测试
			MOTO_run_fun();	//转盘转动
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
	
		case BUTTON_DOWN:
			bell_fun(1);	//响一下
			MOTO_stop_fun();	//转盘停止
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
		
		case BUTTON_LEFT:
			bell_fun(1);	//响一下
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
		
		case BUTTON_RIGHT:
			bell_fun(1);	//响一下
			printf("%d\r\n",button_dat);	//串口显示对应按键编码
			break;
	}
}

网口/TCP/MQTT部分

管脚
PB^0/TIM3_CH3/网口

某些代码意思

英文(打印信息) 含义
10M,100M PHY速度(与下面一起的)
PHY Speed is: PHY 速度为:
PHY Init Successful! PHY 初始化成功!
Network cable is not connected! 网线未连接!
Please connect the network cable. 请连接网线。
Network cable is now connected. 网络电缆现已连接。
Network cable is unplugged! 网线被拔掉了!
DHCP IP address: DHCP IP地址:(地址一般是Nano机有线网络IPv4地址)
DHCP IP GOT IP已经得到
DHCP IP address wait timeout DHCP IP 地址等待超时
Static IP address: 静态 IP 地址:
Connect to Server … 连接到服务器…
Connect to Server Error ! 连接服务器错误!
can not create tcp pcb 无法创建 tcp pcb
Connected to Server Successful 连接服务器成功
connected server flag set: 1 连接的服务器标志集:1
Memory allocate structure “s_es” Error ! 内存分配结构"s_es" 错误 !
Server not found 找不到服务器
have one tcp client error ! 有一个 TCP 客户端错误!
close connection 紧密联系
connected server flag set: 0 关闭连接已连接的服务器标志集:0
sign_in 登入
MQTT_sign_in MQTT登入
topic_in 服务器登入
MQTT_topic_in MQTT服务器登入
Connent command send OK 连接命令发送OK
  • netconf.c/stm32f4x7_ eth_ bsp.c/tcp_ echoclient.c 这3个主要文件是关于服务器/MQTT等
/*************************MQTT.c*************************/
u8 MQTT_race_TOPIC_bit;	//NAn0主机是否识别到结果标志位
u8 MQTT_race_TOPIC_dat;	//MQTT_race_TOPIC_dat是识别物块的结果  0x30  0x31  0x32  0x33

//发布消息
//buff:数据包数组
//dup :重发标志
//qos :服务质量等级
//retain:保留标志
//topic:主题如“a/c”
//msg:消息
物品名 对应数据(前面13字节一样)
白菜 30 0C 00 09 63 6F 6C 6C 65 63 74 30 36 32
芬达 30
打火机 33
纸箱 30
完整的鱼 32
药片 31
鱼骨头 32
西红柿 32
可乐易拉罐 30
香蕉 32
  • 托盘是从小车出发点开始: 0—1—2—3 集中箱是从有害垃圾箱开始:0—1—2—3
最后一个数据表示 对应托盘号(命令) 类别 对应集中箱号()(命令)
0x30(第4个托盘) 3 可回收物 3
0x31(第1个托盘) 0 有害垃圾 0
0x32(第3个托盘) 2 厨余垃圾 2
0x33(第2个托盘) 1 其他垃圾 1
  • 转盘宏定义全局
宏定义 含义
# define TASK_MUN 10 控制数组行的大小
# define GARBAGE_STATE 0 垃圾状态(即数组第1列)
# define GARBAGE_TYPE 1 垃圾类型(即数组第2列)
# define TURNTABLE_COUNT 2 转盘计数(即数组第3列)
# define EMPTY 0
# define WAIT_CAMERA 1 等待识别
# define CAMERA_OK 2 已经识别
# define CAMERA_TIME_OUT 3 识别超时
# define WAIT_ENGINE 4 等待舵机推
# define PUSH 5 推的过程中
# define ARRIVE_OK 6 已经到达托盘
# define SEND_OUT 7 已经发送命令给小车
# define TRANSPORT 8 被小车拿走运送中
/*************************task.c*************************/
//确认垃圾状态与类别
void MQTT_race_topic_data_fun(u8 type)
{
	u8 i;
	u8 type_data;
	
//	type_data = type - 0x30;//因为MQTT 发过来的数据是  0x30  0x31  0x32  0x33   所以这个数据要变成0123  就要减去0x30
	if(type-0x30==0){type_data=3;}	//可回收垃圾
	if(type-0x30==1){type_data=0;}	//有害垃圾
	if(type-0x30==2){type_data=2;}	//厨余垃圾
	if(type-0x30==3){type_data=1;}	//其他垃圾
	
	for(i=0;i<TASK_MUN;i++)//从0开始查看  垃圾的状态  如果是等待识别的,就记录数据
	{
		if(task_buf[i][GARBAGE_STATE]==WAIT_CAMERA)//如果是等待识别
		{
			task_buf[i][GARBAGE_TYPE] = (u32)type_data;//确定垃圾类别
			task_buf[i][GARBAGE_STATE] = CAMERA_OK;			//是识别后的状态
			//printf("MQTT RACE\r\n");										//串口打印
			
			if(type_data==0)
				LCD_ShowString_fun("2:MQTT_race = 0");//屏上显示(有害垃圾)
			if(type_data==1)
				LCD_ShowString_fun("2:MQTT_race = 1");//屏上显示(其他垃圾)
			if(type_data==2)
				LCD_ShowString_fun("2:MQTT_race = 2");//屏上显示(厨余垃圾)
			if(type_data==3)
				LCD_ShowString_fun("2:MQTT_race = 3");//屏上显示(可回收垃圾)
			break;
		}
		else	//如果没有物在第一个光电管和第二个光电管之间  屏幕显示错误
		{
			LCD_ShowString_fun("MQTT_race_late");						//屏上显示表示MQTT_race_late 说明给消息的时间不对了  已经超过了第二个光电管
		}
	}
}

转盘电机

管脚 对应 模式
PD^4 RUN 输出,推挽,无上下拉
PD^13(TIM4_CH2) BACK 同上
PA^8(TIM1_CH1) PWM_STM32(产生PWM) 复用,推挽,无上下拉
PD^2(D2/UART5 RX/TIM3 ETR)外部时钟做时钟源 CODE1 复用,开漏,无上下拉

由于电机那接了一个 74HC02D 与非门芯片(即输入1输出0,输入0输出1),电机低电平才会动,故在配置 PWM时需要配成 PWM1模式+高电平极性 设置占空比100(即一直输出高电平,然后经过芯片就变成一直输出低电平驱动电机转动)

尝试:把极性设为低电平会发现波形是一直低电平,经过与非门也就是一直高电平

转动是通过PID算法计算的(看不懂)

/*************************task.c*************************/
//==========speed_PID==========

u16 speed_new;
float SpeedSet_dat;	//设置的速度
float Kp = 10;//pi控制系数
float Ki = 1;//pi控制系数(没有用到d)

float e;	//pid偏差
float e1;
float e2;
float duk;	//pid输出值
float uk;
float uk1;
float out;
float num;	//实际速度
u16 PWMTime;	//脉冲宽度

void timer5_speed_pid_fun(u16 speed)
{
	u8 i;
	
	speed_new = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3,0);
	MOTO_count = MOTO_count + speed_new;
	
	
	for(i=0;i<TASK_MUN;i++)
	{
		if(task_buf[i][GARBAGE_STATE]==WAIT_ENGINE)
		{
			task_buf[i][TURNTABLE_COUNT] = task_buf[i][TURNTABLE_COUNT] + speed_new;
		}
	}
	

	num = (float)speed_new;
	SpeedSet_dat = speed;
	
	e=SpeedSet_dat-num;//设置速度-实际速度,两者的差值 
	duk=(Kp*(e-e1)+Ki*e)/100;//只调节PI
	uk=uk1+duk;//uk=u(k-1)+Δuk
	out=(int)uk;//取整后输出
	if(out>90)	//设置最大限制
		out=90;
	else if(out<0)//设置最小限制
		out=0;
	uk1=uk;		  //为下一次增量做准备
	e2=e1;
	e1=e;
	
	PWMTime=(u16)out;  //out对应于PWM高电平的时间

	MOTO_pwm_set(PWMTime);
}
void PID_ram_init(void)
{
    //pid偏差
	e = 0;
    e1 = 0;
    e2 = 0;
    //pid输出值
    uk = 0;
    uk1 = 0;
    duk = 0;
	num = 0;
	out = 0;
}
/*************************STM32F40x_ GPIO_ Init.h*************************/
//电机
# define RUN_OFF		(GPIOD->BSRRH = GPIO_Pin_4)	//管脚设为低电平(相当于停止)
# define RUN_ON		(GPIOD->BSRRL = GPIO_Pin_4)	//管脚设为高电平(相当于启动)    
/*************************main.c*************************/
//轮盘速度(越大速度越快)
# define MOTO_SPEED_SET	30
/*************************MOTO.c*************************/
//转盘电机PWM初始化(这里没设置互补通道故相当于普通PWM模式)
void TIM1_PWM_Init_PA8(u32 arr,u32 psc)
{ 
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;	//定义结构体
	TIM_OCInitTypeDef TIM_OCInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);	//使能TIM1时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);	//使能A组时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;	//PA^8
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	//无上下拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//初始化
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1);	//PA^8复用为TIM1_CH1
	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//分频因子为1
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
	TIM_TimeBaseStructure.TIM_Prescaler = psc;//Timer clock = sysclock /(TIM_Prescaler+1) = 168M 
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //此参数不用需配置为0(只有高级定时器才有)
	TIM_TimeBaseStructure.TIM_Period = arr; //Period = (TIM counter clock / TIM output clock) - 1 = 20K 
	
	TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure); 
	
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 	//PWM1模式(CNT<CCR有效,反之无效)
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //互补输出使能。开启OCN输出到对应的管脚
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补输出使能。开启OCN输出到对应的管脚   
	TIM_OCInitStructure.TIM_Pulse = (arr+1)/2; //重装载值的一半
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //主通道的输出极性
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High; //互补通道的输出极性
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //空闲状态输出高电平
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //空闲状态输出低电平
	
	TIM_OC1Init(TIM1,&TIM_OCInitStructure);	//使能
	TIM_Cmd(TIM1,ENABLE);
	
	TIM_CtrlPWMOutputs(TIM1,ENABLE);	//高级定时器才有  必须打开
}

//转盘电机控制初始化     PWM:PA8		RUN:PD4		BACK:PD13
void MOTO_init(void)
{
	GPIO_init(GPIO_D,GPIO_Pin_4,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//D4			RUN
	GPIO_init(GPIO_D,GPIO_Pin_13,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);//D13		BACK
	RUN_OFF;
	BACK_OFF;
	//计算:100*84/168000000 = 0.00005s = 0.05ms (1KHz = 1ms --> 20KHz = 0.05ms)
    //右边/20,左边*20
	TIM1_PWM_Init_PA8(100-1,84-1);	//频率 20kHz  占空比  0-100

	TIM_SetCompare1(TIM1,100);//最大0   最小100   PWM输出经过了数字电路  低电平有效
}
//识别到RFID后开始设置速度,pid初始化,准备启动
void MOTO_run_fun(void)
{
	PID_ram_init();
	moto_speed = MOTO_SPEED_SET;	//速度
	moto_run_bit = 1;
}

//轮盘停止(暂时没用到)
void MOTO_stop_fun(void)
{
	moto_run_bit = 0;
	RUN_OFF;
	MOTO_pwm_set(0);
}
//转盘电机PWM输出
void MOTO_pwm_set(u16 speed)
{
	RUN_ON;
	
	TIM_SetCompare1(TIM1,100-speed);//因为电平有一个 74HC02 所以  电平是反的   低电平 开始转
}

//编码器 计数 TIM3 PD2 计数程序初始化  用于转盘电机编码器
void TIM3_PD2_encoder_Init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

	GPIO_init(GPIO_D,GPIO_Pin_2,GPIO_Mode_AF,GPIO_OType_OD,GPIO_PuPd_NOPULL);	//复用,开漏,无上下拉
	
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_TIM3);	//复用PD^2为TIM3
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能TIM3时钟

	TIM_TimeBaseStructure.TIM_Prescaler = 0x00; //预分频系数
	TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //重装载值
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);  // 定时器3初始化

	TIM_ETRClockMode2Config(TIM3,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted, 0);	//配置ETR外部输入触发模式,外部输入模式2

	TIM_SetCounter(TIM3, 0);	//清0
	TIM_Cmd(TIM3, ENABLE); 
}
/*************************STM32F40x_Timer_eval.c*************************/
//定时器中断服务函数
//定时20ms
void TIM5_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)	//==1表示发生溢出中断
    {
        TIM_ClearITPendingBit(TIM5, TIM_IT_Update);	//清除标志位
        timer5_int_bit = 1;
        if(moto_run_bit == 1)
        {
            timer5_speed_pid_fun(moto_speed);	//在这轮盘一直动
        }

        PG9_F;	//暂不知道这个什么用处
    }
}

舵机

管脚 对应 模式 对应舵机(小车出发点起)
PA^6(TIM13_CH1) DOUJI1 复用,推挽,无上下拉 舵机1
PC^8(TIM8_CH3) DOUJI2 同上 舵机2
PD^12(TIM4_CH1) DOUJI3 同上 舵机3
PC^9(TIM8_CH4) DOUJI4 同上 舵机4
  • 对于180°的舵机,一般来说周期为 20ms(频率为50Hz),频率越高,舵机反应速度越快,但是力的输出却越小,脉宽(一个周期内高电平持续时间)为 500–2500us。其中为1500us使得舵机转轮处于中间位置,可以理解为90°的位置,为500-1500us和1500–2500us之间分别会朝着0–90°和90–180°的方向旋转(慢慢增大占空比的时间延长点可以把舵机旋转速度降下来)
  • 本轮盘4个舵机型号是 MG995,下面是它的一些需要注意的地方
t占空比高电平持续时间(频率20ms) 角度
0.5ms 舵机会转动 0 °
1.0ms 舵机会转动 45°
1.5ms 舵机会转动 90°
2.0ms 舵机会转动 135°
2.5ms 舵机会转动180°
/*************************main.c*************************/
//舵机推和收的PWM参数
# define PULL1		275//290
# define SHRINK1	100//120

# define PULL2		280//265
# define SHRINK2	100//105

# define PULL3		280//280
# define SHRINK3	100//115

# define PULL4		285//255
# define SHRINK4	100//95
/*************************MOTO.c*************************/
//舵机1(剩下3个舵机配置一样只是引脚不一样)
void TIM13_PWM_Init_PA6(u32 arr,u32 psc)
{ 
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;	//定义结构体
	TIM_OCInitTypeDef TIM_OCInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13,ENABLE);	//使能TIM13时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);	//使能A组时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推挽
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;	//PA^6
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	//无上下拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM13);	//复用引脚为TIM13
	
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;	//分频因子
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
	TIM_TimeBaseStructure.TIM_Prescaler = psc;	//预分频系数
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; 
	TIM_TimeBaseStructure.TIM_Period = arr;	//重装载值
	
	TIM_TimeBaseInit(TIM13,&TIM_TimeBaseStructure);
	
	//互补通道也就是含N的结构体成员可写可不写没影响(如果不需要互补通道的话)
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;	//PWM1模式(cnt<rcc电平有效;反之无效)
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = (arr+1)/2;	//重装载的一半
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;	//有效电平为高
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	
	TIM_OC1Init(TIM13,&TIM_OCInitStructure);
	TIM_Cmd(TIM13,ENABLE);	//使能TIM13
	
	TIM_CtrlPWMOutputs(TIM13,ENABLE);	//此函数仅高级定时器才需要加(其他定时器可省略)
}

//4个舵机初始化
void pwm_init(void)
{
	//PA6  	ok
	//PC8	ok
	//D12	OK
	//C9	OK
	//下面如果想更加精准点可以通过公式计算即可
	TIM13_PWM_Init_PA6(5000-1,335-1);	// 5000*335/84000000 ≈ 0.0199s ≈ 20ms(50Hz)
	TIM8_PWM_Init_PC8(5000-1,670-1);	// 5000*670/168000000 ≈ 0.0199s ≈ 20ms(50Hz) 为什么是670? 因为可以通过分母扩大2倍分子也扩大2倍来保持结果不变 
	TIM4_PWM_Init_PD12(5000-1,335-1);
	TIM8_PWM_Init_PC9(5000-1,670-1); 
	
	
	//750=2.5ms	125=0.5ms
	
	steering_engine_1_shrink();
	steering_engine_2_shrink();
	steering_engine_3_shrink();
	steering_engine_4_shrink();
}
//舵机1推---收(剩下3个舵机配置一样占空比也是一样)
void steering_engine_1_push(void)
{
	u16 pwm_data;
	//printf("PWM1_open\r\n");
	
	
	pwm_data = SHRINK1;
	while(pwm_data<=PULL1)	//pwm_data:100 PULL1:275(占空比范围:2%~5.5%)
	{
        //最小占空比:100/5000 = 0.02 = 2%
        //最大占空比:275/5000 = 0.055 = 5.5%
		TIM_SetCompare1(TIM13,pwm_data);
		pwm_data++;
		delay_ms(2);//决定了舵机推出去的速度(延时越大速度越慢)
	}
	
}

void steering_engine_1_shrink(void)
{
	TIM_SetCompare1(TIM13,SHRINK1);//占空比:100/5000 = 0.02 = 2%
	//printf("PWM1_close\r\n");
}

蜂鸣器

管脚 对应 模式
PG^1 BELL 输出,推挽,无上下拉
/*************************STM32F40x_ GPIO _Init.h*************************/
//蜂鸣器
# define BELL_OFF	(GPIOG->BSRRH = GPIO_Pin_1)	//管脚设为低电平(不响)
# define BELL_ON		(GPIOG->BSRRL = GPIO_Pin_1)	//管脚设为高电平(响),因为原理图那个是NPN三极管需要高电平导通
//没用到(蜂鸣器的)
# define	BELL_F		GPIOG->ODR^=GPIO_Pin_2	

光电管

管脚 对应 模式
PF^1 WEIZHI1(托盘的反射管) 输入,开漏,上拉(因为距离太近,有光是1,无光是0)
PF^2 WEIZHI2(托盘的反射管) 同上
PF^3 WEIZHI3(托盘的反射管) 同上
PF^4 WEIZHI4(托盘的反射管) 同上
PA5 SW1(第一个光电对射) 输出,开漏,无上下拉
PD7 SW2(第二个光电对射) 同上
PE8 SW3 同上
PE10 SW4 同上
PF12 SW6 同上
PF11 SW5 同上
  • 轮盘有 2 个对射管 , 4 个反射管(分别在4个托盘上用来识别是否有物体在托盘); 有光是1 无光是0

  • 第一个光电对射管是判断物块是否已经进入识别区域(需要MQTT把识别结果发送回来第二个光电对射管才会进行对应操作); 第二个红外对射光电管` 判断物块是否已经通过识别区域(如果识别通过则将这个物块的类别和位置计数清零)

/*************************STM32F40x_ GPIO _Init.h*************************/
//光电对射
# define READ_DUIGUAN1		GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)	//读取第一个光电对射的电平状态
# define READ_DUIGUAN2		GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_7)	//读取第二个光电对射的电平状态
# define READ_DUIGUAN3		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_8)	//这4个没用到暂时不用管
# define READ_DUIGUAN4		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_10)
# define READ_DUIGUAN5		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_12)
# define READ_DUIGUAN6		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_11)

//托盘位置的反射管
# define READ_WEIZHI1		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_1)
# define READ_WEIZHI2		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_2)
# define READ_WEIZHI3		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_3)
# define READ_WEIZHI4		GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_4)
/*************************STM32F40x_ GPIO _Init.c*************************/
//光电对射管
void Photoelectric_switch_gpio_init(void)
{
	//1:PF1
	//2:PF2
	//3:PF3
	//4:PF4
//反射管脚
	GPIO_init(GPIO_F,GPIO_Pin_1,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_UP);//PF1   //有光是1  无光是0   必须是上拉模式   不然距离有太近   不到10cm
	GPIO_init(GPIO_F,GPIO_Pin_2,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_UP);//PF2
	GPIO_init(GPIO_F,GPIO_Pin_3,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_UP);//PF3
	GPIO_init(GPIO_F,GPIO_Pin_4,GPIO_Mode_IN,GPIO_OType_OD,GPIO_PuPd_UP);//PF4
	

//第一个对射管脚	
   GPIO_init(GPIO_A,GPIO_Pin_5,GPIO_Mode_OUT,GPIO_OType_OD,GPIO_PuPd_NOPULL);//PA5
	(GPIOA->BSRRL = GPIO_Pin_5);	//设为高电平
	
	
//第二个对射管脚
   GPIO_init(GPIO_D,GPIO_Pin_7,GPIO_Mode_OUT,GPIO_OType_OD,GPIO_PuPd_NOPULL);//PD7
	(GPIOD->BSRRL = GPIO_Pin_7);
	
	GPIO_init(GPIO_E,GPIO_Pin_8,GPIO_Mode_OUT,GPIO_OType_OD,GPIO_PuPd_NOPULL);//PE8
	(GPIOE->BSRRL = GPIO_Pin_8);
	
	GPIO_init(GPIO_E,GPIO_Pin_10,GPIO_Mode_OUT,GPIO_OType_OD,GPIO_PuPd_NOPULL);//PE10
	(GPIOE->BSRRL = GPIO_Pin_10);
	
	GPIO_init(GPIO_F,GPIO_Pin_12,GPIO_Mode_OUT,GPIO_OType_OD,GPIO_PuPd_NOPULL);//PF12
	(GPIOF->BSRRL = GPIO_Pin_12);
	
	GPIO_init(GPIO_F,GPIO_Pin_11,GPIO_Mode_OUT,GPIO_OType_OD,GPIO_PuPd_NOPULL);//PF11
	(GPIOF->BSRRL = GPIO_Pin_11);
}
/*************************task.c*************************/
//两个对射管电平状态存储数组
u8 Photoelectric_switch_new[2];//两个光电对射
u8 Photoelectric_switch_old[2];

//托盘的反射管电平状态存储数组
u8 position_switch_new[4];//四个光电反射
u8 position_switch_old[4];

u8 interrupt1;	//经过第一个光电对射标志位
u8 interrupt2;	//经过第二个光电对射标志位


u8 weizhi1_interrupt_in;	//托盘有物品
u8 weizhi2_interrupt_in;
u8 weizhi3_interrupt_in;
u8 weizhi4_interrupt_in;

u8 weizhi1_interrupt_out;	//托盘没有物品
u8 weizhi2_interrupt_out;
u8 weizhi3_interrupt_out;
u8 weizhi4_interrupt_out;


//读取光电管的变化
void read_interrupt_fun(void)
{
	//第一个光电对射
	Photoelectric_switch_new[0] = READ_DUIGUAN1;	//读取最新的电平状态(第一次读取时是没有物体的所以一定是1,当有物体经过时变0)
	if((Photoelectric_switch_new[0]==0)&&(Photoelectric_switch_old[0]==1))
	{
		interrupt1 = 1;	//标志位置1表示经过了第一个对射
		//printf("interrupt1\r\n");
	}
	Photoelectric_switch_old[0] = Photoelectric_switch_new[0];	//把当前状态存储起来
	
	//第二个光电对射
	Photoelectric_switch_new[1] = READ_DUIGUAN2;
	if((Photoelectric_switch_new[1]==0)&&(Photoelectric_switch_old[1]==1))
	{
		interrupt2 = 1;
		//printf("interrupt2\r\n");
	}
	Photoelectric_switch_old[1] = Photoelectric_switch_new[1];
	
	
	//第一个位置
	position_switch_new[0] = READ_WEIZHI1;
	if((position_switch_new[0]==0)&&(position_switch_old[0]==1))
	{
		weizhi1_interrupt_in = 1;
		//printf("weizhi1_in\r\n");
	}
	if((position_switch_new[0]==1)&&(position_switch_old[0]==0))
	{
		weizhi1_interrupt_out = 1;
		//printf("weizhi1_out\r\n");
	}
	position_switch_old[0] = position_switch_new[0];
	
	
	
	//第二个位置
	position_switch_new[1] = READ_WEIZHI2;
	if((position_switch_new[1]==0)&&(position_switch_old[1]==1))
	{
		weizhi2_interrupt_in = 1;
		//printf("weizhi2_in\r\n");
	}
	if((position_switch_new[1]==1)&&(position_switch_old[1]==0))
	{
		weizhi2_interrupt_out = 1;
		//printf("weizhi2_out\r\n");
	}
	position_switch_old[1] = position_switch_new[1];
	
	
	
	//第三个位置
	position_switch_new[2] = READ_WEIZHI3;
	if((position_switch_new[2]==0)&&(position_switch_old[2]==1))
	{
		weizhi3_interrupt_in = 1;
		//printf("weizhi3_in\r\n");
	}
	if((position_switch_new[2]==1)&&(position_switch_old[2]==0))
	{
		weizhi3_interrupt_out = 1;
		//printf("weizhi3_out\r\n");
	}
	position_switch_old[2] = position_switch_new[2];
	
	
	
	//第四个位置
	position_switch_new[3] = READ_WEIZHI4;
	if((position_switch_new[3]==0)&&(position_switch_old[3]==1))
	{
		weizhi4_interrupt_in = 1;
		//printf("weizhi4_in\r\n");
	}
	if((position_switch_new[3]==1)&&(position_switch_old[3]==0))
	{
		weizhi4_interrupt_out = 1;
		//printf("weizhi4_out\r\n");
	}
	position_switch_old[3] = position_switch_new[3];
}
//经过第一个光电对射时执行动作
void Photoelectric_switch_1_fun(void)//第一个光电开关信号处理
{
	u8 i;
	
	for(i=0;i<TASK_MUN;i++)//从0开始查看  垃圾的状态  为空的  设置成下一个状态  就是等待摄像头识别
	{
		if(task_buf[i][GARBAGE_STATE]==EMPTY)//如果是空
		{
			task_buf[i][GARBAGE_STATE] = WAIT_CAMERA;//垃圾状态变成等待摄像头识别
			//printf("position 1 int\r\n");
			break;
		}
	}
}


//经过第二个光电对射时执行动作
void Photoelectric_switch_2_fun(void)//第2个光电开关信号处理
{
	u8 i;
	
	for(i=0;i<TASK_MUN;i++)
	{
		if(task_buf[i][GARBAGE_STATE]==CAMERA_OK)
		{
			task_buf[i][GARBAGE_STATE] = WAIT_ENGINE;//等待舵机推
			task_buf[i][TURNTABLE_COUNT] = 0;//计数清零(准备开始计数脉冲)
			//printf("position 2 int\r\n");
			break;
		}
	}
}
//位置有东西
void position_in_fun(void)
{
	u8 i;
	
	for(i=0;i<TASK_MUN;i++)
	{
		if(task_buf[i][GARBAGE_STATE]==PUSH)	//判断当前垃圾状态是否为推的过程,是则把状态改为到达托盘
		{
			task_buf[i][GARBAGE_STATE]=ARRIVE_OK;	//垃圾状态为到达托盘
			break;
		}
	}
}


//位置没有东西
void position_out_fun(void)
{
	u8 i;
	
	for(i=0;i<TASK_MUN;i++)
	{
		if(task_buf[i][GARBAGE_STATE]==ARRIVE_OK)	//当垃圾状态为到达托盘且被车取走时
		{
			task_buf[i][GARBAGE_STATE]=TRANSPORT;//状态为被小车拿走运送中
			break;
		}
	}
}

RFID

管脚 对应 模式
PG^15 MISO(SPI数据输入) 输入,推挽,无上下拉
PB^9 SCK(SPI时钟) 输出,推挽,无上下拉
PG^13 MOSI(SPI数据输出) 同上
PG^12 NSS(SPI片选) 同上
PB^8 RST(复位) 同上
  • RC522是13.56MHz非接触通讯高集成度的芯片,通过SPI接口与S50卡通讯,完成卡的识别,读卡,写卡等操作

串口2无线通信

管脚 对应 模式
PA^3 USART2_RX 复用,推挽,上拉
PD^5 USART2_TX 复用,推挽,上拉
  • 目前只有小车会给转盘发命令 就一种命令 就是小车的任务完成
/*************************task.c*************************/
u8 car_busy_bit = 0;	//小车忙的标志位 0:空闲 1:忙
/*************************STM32H40x_ Usart eval.h*************************/
# define UART2_RACE_MUN	100	//串口2数组大小
u8 uart2_cmd_buf[100];	//命令数组(存储usart2_race_buf的数据)
u8 uart2_cmd_count;	//数组索引
u8 usart2_send_buf[100];	//USART2发送数据数组
u8 usart2_race_buf[UART2_RACE_MUN];	//接收USART2接收到的数据
u8 usart2_race_over_bit;	//接收一帧完成标志位
u8 usart2_race_count;	//数组索引



/*************************STM32H40x_ Usart eval.c*************************/
//USART2初始化
void Uart2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;	//定义结构体
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	

  /* Enable GPIO clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);	//使能对应时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	
  /* Connect PXx to USARTx_Tx*/
  GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_USART2);	//将对应管脚复用为USART2
	
  /* Connect PXx to USARTx_Rx*/
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);	
	
	/* Configure USART Tx as alternate function  */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推挽
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;	//上拉
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	//复用

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	//PD^5
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOD, &GPIO_InitStructure);

  /* Configure USART Rx as alternate function  */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;	//PA^3
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	

	/* Configure and enable I2C DMA TX Stream interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;	//串口中断通道
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//先占优先级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//子优先级
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//使能通道
  NVIC_Init(&NVIC_InitStructure);	//初始化
	
	
  /* Enable USART clock */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//串口2的时钟使能
	
	//USART2 初始化设置
	USART_InitStructure.USART_BaudRate = 115200;//一般设置为115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8bit
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位1位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无流控
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//接收/发送
	
	/* USART2 configuration */
	USART_Init(USART2, &USART_InitStructure);
  
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启接收中断
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//开启空闲总线中断
	
	/* Enable USART2 */
	USART_Cmd(USART2, ENABLE);
	
	USART_ClearFlag(USART2, USART_FLAG_TC);	//清除发送完成标志位
}



//USART2发送多个字节
void Uart2_send_data(unsigned char *dat, unsigned short len)
{
	uart2_race_init();
	
	while(len--)
	{
		while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);//传输完成
		USART2->DR = (*dat & (uint16_t)0x01FF);
		dat++;
	}
}




//USART2串口中断服务函数
void USART2_IRQHandler(void)
{
	u8 Res;
	u8 temp;
	
	temp = temp;
	
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断
	{
		Res = USART_ReceiveData(USART2);//(UART4->DR);	//读取接收到的数据
		
		usart2_race_buf[usart2_race_count] = Res;	//将接收的数据保存到数组
		usart2_race_count++;
		if(usart2_race_count==UART2_RACE_MUN)	//如果接收的数量超过定义的最大大小则为0
		{
			usart2_race_count = 0;
		}
	}
	if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)  //空闲中断
	{
		//清除标志位
		temp = USART2->SR;	//先读USART_SR,然后写入USART_DR
		temp = USART2->DR;	
		
		usart2_race_over_bit = 1;
	}
}


//串口2接收标志位/数组索引清零
void uart2_race_init(void)
{
	usart2_race_over_bit = 0;
	usart2_race_count = 0;
}
//串口2 接收数据数组+接收标志位/数组索引 清零
void uart2_buf_init(void)
{
	u8 i;
	
	for(i=0;i<UART2_RACE_MUN;i++)
	{
		usart2_race_buf[i] = 0;
	}
	
	usart2_race_over_bit = 0;
	usart2_race_count = 0;
}
//拷贝串口2的数据到命令数组
void uart2_copy_data(void)
{
	u8 i;
	
	for(i=0;i<usart2_race_count;i++)
	{
		uart2_cmd_buf[i] = usart2_race_buf[i+3];
	}
	
	uart2_cmd_count = usart2_race_count;	//命令数组的索引等于接收数组的索引
	
	uart2_buf_init();	//接收数组初始化
}
//处理小车发来的命令
void uart2_race_explan(void)
{
	//后期补充
	if(usart2_race_buf[0]==0x01)	//判断数组第一个元素是不是自己的ID 1:转盘
	{
		if(usart2_race_buf[1]==0xff)	//判断数组第二个元素是不是 0xff;这个在小车的代码里有写 0xff表示已完成任务
		{
			car_busy_bit = 0;//小车忙标志清除
		}
	}
}
/*
轮盘ID:01
托盘编号 	1234
集中站标号	1234
*/
//发送命令给小车(当托盘上有物品时才会通知小车)
void send_to_car_cmd(u8 tuopan,u8 jizhongzhan)
{
	u8 cmd_buf[5];
	
	cmd_buf[0] = 2;//发送小车的ID
	cmd_buf[1] = 0x01;	//第二个字节是需要小车干嘛(暂时定义了:0x01---轮盘->集中站;后期可以添加:0x02---轮盘->垃圾桶等等)
	cmd_buf[2] = tuopan;	//几号托盘
	cmd_buf[3] = jizhongzhan;	//到哪个集中站
	
	Uart2_send_data(cmd_buf,4);//发送函数
	
	bell_fun(1);	//蜂鸣器响
}
/*************************task.c*************************/
u32 coder_mun_buf[4] = {DIS_MUN1,DIS_MUN2,DIS_MUN3,DIS_MUN4};	//存储走到4个舵机位置脉冲的计数值

//检查小车是否在运送还是在起点
void check_car_fun(void)
{
	u8 i;
	
	if(car_busy_bit==0)	//当小车处于闲的状态
	{
		for(i=0;i<TASK_MUN;i++)
		{
			if(task_buf[i][GARBAGE_STATE]==ARRIVE_OK)//物品在托盘位置上
			{
				send_to_car_cmd(task_buf[i][GARBAGE_TYPE],task_buf[i][GARBAGE_TYPE]);//发送命令给小车 第一个参数是托盘  第二个参数是集中站
				car_busy_bit = 1;//小车忙标志位置1
				task_buf[i][GARBAGE_STATE] = SEND_OUT;	//已经发送命令给小车
				
				if(task_buf[i][GARBAGE_TYPE]==0)
					LCD_ShowString_fun("6:send_car 0,0");//屏上显示(即第一个托盘->到第一个集中箱)
				if(task_buf[i][GARBAGE_TYPE]==1)
					LCD_ShowString_fun("6:send_car 1,1");//屏上显示(即第二个托盘->到第二个集中箱)
				if(task_buf[i][GARBAGE_TYPE]==2)
					LCD_ShowString_fun("6:send_car 2,2");//屏上显示(即第三个托盘->到第三个集中箱)
				if(task_buf[i][GARBAGE_TYPE]==3)
					LCD_ShowString_fun("6:send_car 3,3");//屏上显示(即第四个托盘->到第四个集中箱)

				break;
			}
		}
	}
}

//检查物品是否已经走到对应的舵机前(如果是则推)
void coder_count_chack_fun(void)
{
	u8 i;
	u8 type;
	
	for(i=0;i<TASK_MUN;i++)
	{
		if(task_buf[i][GARBAGE_STATE]==WAIT_ENGINE)	//判断状态是不是等待舵机推
		{
			type = (u8)task_buf[i][GARBAGE_TYPE];	//读取物品类型然后对应的舵机推(0就第一个舵机,1是第二个舵机,以此类推)
			if(task_buf[i][TURNTABLE_COUNT] >= coder_mun_buf[type])	//判断脉冲值是不是>=规定脉冲
			{
				//printf_fun();
				task_buf[i][GARBAGE_STATE] = PUSH;	//垃圾状态为推的过程中
				switch(type)
				{
					case 0:
						LCD_ShowString_fun("4:tui0");//屏上显示
						steering_engine_1_push();//舵机1推
						delay_ms(500);
						steering_engine_1_shrink();//舵机1收
						break;
					case 1:
						LCD_ShowString_fun("4:tui1");//屏上显示
						steering_engine_2_push();//舵机2推
						delay_ms(500);
						steering_engine_2_shrink();//舵机1收
						break;
					case 2:
						LCD_ShowString_fun("4:tui2");//屏上显示
						steering_engine_3_push();//舵机3推
						delay_ms(500);
						steering_engine_3_shrink();//舵机1收
						break;
					case 3:
						LCD_ShowString_fun("4:tui3");//屏上显示
						steering_engine_4_push();//舵机4推
						delay_ms(500);
						steering_engine_4_shrink();//舵机1收
						break;
				}
			}
		}
	}
}
/*************************main.c*************************/
//转盘推杆位置(计数值)
# define DIS_MUN1	29500//33046	//走到舵机1位置所需脉冲
# define DIS_MUN2	36500//39132	//走到舵机2位置所需脉冲
# define DIS_MUN3	44000//46091	//走到舵机3位置所需脉冲
# define DIS_MUN4	50500//52348	//走到舵机4位置所需脉冲

转盘注意问题

  • STM32F40x_Timer_eval.c 定时器5计算20ms定时步骤

  • 这个数组是代码的主要部分

  • 注意转盘有时候会接收不了MQTT发送回来的数据,初步怀疑可能服务器不稳定,重启然后等待10秒左右再放物品(需要没有收到心跳时才可以,如果上电后一直收到心跳则继续重启直到没收到心跳再滴卡),如果不行还得顺便把Nano主机那个识别垃圾程序重启一下(反正两个东西多重启即可)