本程序编写基于正点原子STM32F407开发板。
本文使用的扫码模块是下面这个品牌。
扫码模块的应用场景非常广泛,我们可以上百度搜索一下:
等等。
今天就来说说如何在开发板上实现控制它吧,打开数据手册看引脚配置。
该模块是基于串口开发,本例程只接了上述的5个引脚,其余的没有接入。
按手册提供的说明,只要我们每次扫码之前将nTrig管脚拉低,即可激活扫码模块,我们先接上Uart测试下效果,在没有接nTrig脚的时候,是下面这个样子,此时为非扫码模式。
当将nTrig引脚接地时,是下面这个样子,此时扫码模式激活,只需随便拿一个条码接近光源就可以得到数据输出。
关于模块的详细配置信息,可以参考数据手册:LV3096集成手册。
https://download.csdn.net/download/morixinguan/11603322
接下来开始配置CubeMX,驱动这个模块。
一、配置IO和串口以及OS
1.1、配置普通IO
其中PE0就是图中的nTrig引脚,PE3为按键,当按键按下时,触发nTrig引脚为低电平。
PF8为蜂鸣器,即成功收到扫码模块返回的数据后,会一声,PF9和PF10为指示灯。
1.2、配置串口
串口2用来打印信息。
串口3是扫码模块,根据手册提示,该模块的默认波特率为9600。
配置全局中断。
1.3、配置FreeRtos
由于我的项目需要带OS,所以顺便也把OS配置上吧,把软件定时器勾上,其它的选项默认即可。
注意,配置了FreeRtos,要把这里的时钟源改掉,以免冲突。
1.4、配置时钟
默认配置为168MHz
1.5、生成代码
二、编写相关逻辑
在main.c中添加头文件
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* USER CODE END Includes */
在main.c中定义任务和信号量
/* USER CODE BEGIN PV */
//判断当前是否在OS
__IO int start_os = 0;
//扫码任务
osThreadId ScanTaskHandle;
//按键任务
osThreadId KeyTaskHandle;
//得到扫码数据的信号量
SemaphoreHandle_t Scan_Data_Semaphore = NULL;
/* USER CODE END PV */
在main.c中定义串口打印函数和任务函数
/* USER CODE BEGIN PFP */
//扫码任务处理
void StartKeyTask(void const * argument);
//按键任务处理
void StartScanTask(void const * argument);
//定义printf的重定向函数fputc,满足串口调试打印
int fputc(int ch, FILE* file)
{
HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 1000);
return ch;
}
/* USER CODE END PFP */
在stm32f4xx_hal_msp.c中串口三的用户定义区域使能串口3接收中断。
/* USER CODE BEGIN USART3_MspInit 1 */
//这里不需要发送
__HAL_UART_DISABLE_IT(huart, UART_IT_TC);
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
/* USER CODE END USART3_MspInit 1 */
在stm32f4xx_it.c中编写中断服务函数
定义串口接收缓存以及计数变量。
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t scan_rx_count;
uint8_t USART3_RX_BUF[UART_RECV_LEN] = {0} ;
/* USER CODE END PV */
编写扫码模块串口中断服务函数
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
//加热器串口中断服务函数
uint32_t ulReturn;
uint8_t Res;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
//进入临界段,不可被中断
if(1 == start_os)
ulReturn = taskENTER_CRITICAL_FROM_ISR();
if((__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
HAL_UART_Receive(&huart3, &Res, 1, 1000);
if('\n' != Res)
{
USART3_RX_BUF[scan_rx_count++] = Res ;
}
else
{
//如果接收的是\n,则上一个接收的数据为'\r'结束
if('\r' == USART3_RX_BUF[scan_rx_count - 1])
{
//添加结束符
USART3_RX_BUF[scan_rx_count - 1] = 0x00 ;
//接收计数清0
scan_rx_count = 0 ;
//给出一个信号量
if(1 == start_os)
xSemaphoreGiveFromISR(Scan_Data_Semaphore, &xHigherPriorityTaskWoken);
}
}
}
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
//退出临界段
if(1 == start_os)
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
/* USER CODE END USART3_IRQn 1 */
}
在main.c中创建信号量和任务函数
创建信号量
//创建一个二值信号量,用于传感器数据的接收处理
Scan_Data_Semaphore = xSemaphoreCreateBinary();
assert_param(NULL != Scan_Data_Semaphore);
创建任务函数
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
osThreadDef(ScanTask, StartScanTask, osPriorityNormal, 0, 128);
ScanTaskHandle = osThreadCreate(osThread(ScanTask), NULL);
osThreadDef(KeyTask, StartKeyTask, osPriorityNormal, 0, 128);
KeyTaskHandle = osThreadCreate(osThread(KeyTask), NULL);
/* USER CODE END RTOS_THREADS */
编写应用逻辑
/* USER CODE BEGIN 4 */
//按键处理任务
void StartKeyTask(void const * argument)
{
while(1)
{
//当按键按下时候,触发扫码模块
if(GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin))
HAL_GPIO_WritePin(START_SCAN_GPIO_Port, START_SCAN_Pin, GPIO_PIN_RESET);
osDelay(200);
}
}
extern uint8_t scan_rx_count;
extern uint8_t USART3_RX_BUF[UART_RECV_LEN] ;
//扫码处理任务
void StartScanTask(void const * argument)
{
start_os = 1 ;
BaseType_t xResult;
uint8_t *__Scan_Data_Cpy = NULL ;
__Scan_Data_Cpy = malloc(UART_RECV_LEN);
assert_param(NULL != __Scan_Data_Cpy);
while(1)
{
//等待获取一个信号量,portMAX_DELAY表示无超时限制
xResult = xSemaphoreTake(Scan_Data_Semaphore, portMAX_DELAY);
if(xResult == pdTRUE)
{
memset(__Scan_Data_Cpy, 0, UART_RECV_LEN);
memcpy(__Scan_Data_Cpy, USART3_RX_BUF, strlen((char *)USART3_RX_BUF));
printf("扫码数据:%s\n",__Scan_Data_Cpy);
memset(USART3_RX_BUF,0,UART_RECV_LEN);
//关闭扫码使能
HAL_GPIO_WritePin(START_SCAN_GPIO_Port, START_SCAN_Pin, GPIO_PIN_SET);
//蜂鸣器鸣叫一声
HAL_GPIO_WritePin(BELL_GPIO_Port,BELL_Pin,GPIO_PIN_SET);
osDelay(150);
HAL_GPIO_WritePin(BELL_GPIO_Port,BELL_Pin,GPIO_PIN_RESET);
}
}
}
/* USER CODE END 4 */
三、运行结果
按下按键KEY1激活扫码模块
拿一个有条码的本子
靠近扫码枪,识别到条形码后,扫码枪会将光源关断,如需要再次使用,需要再按下KEY1按键开启。
串口收到的打印结果,和本子上的条形码的数据是一样的。
Demo例程,后续提供。