STM32作业实现(八)触摸按键TPAD

目录

STM32作业设计
STM32作业实现(一)串口通信
STM32作业实现(二)串口控制led
STM32作业实现(三)串口控制有源蜂鸣器
STM32作业实现(四)光敏传感器
STM32作业实现(五)温湿度传感器dht11
STM32作业实现(六)闪存保存数据
STM32作业实现(七)OLED显示数据
STM32作业实现(八)触摸按键TPAD
STM32作业实现(九)驱动舵机
源码位置

编写Tpad驱动文件(控制舵机开关)


开启所需引脚

开启中断模式

tpad.h

c 复制代码
#ifndef __TPAD_H__
#define __TPAD_H__

#include "main.h"
#include "tim.h"

void tpad_init(void); // 初始化tapd默认值
uint8_t tpad_scan(uint8_t mode);// 获取tpad是否有触摸

#endif

tpad.c

c 复制代码
#include "tpad.h"

uint16_t temp = 0;             // 每次读取数据后存放
uint16_t flag = 0;             // 中断标志位
uint16_t tpad_default_val = 0; // 无接触的值

/*
复位 TPAD

将TPAD按键看作是一个电容,手指按下和不按下电容值有变化
先将GPIO设置为推挽输出,输出0,进行放电,
在设置为GPIO为浮空输入,等待电容充电,并且捕获上拉
*/
void tpad_reset(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  // 将PA1设置为开漏输出
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  // 将PA1设置0,对电容进行放电
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);

  htim5.Instance->SR = 0;  // 清除标记
  htim5.Instance->CNT = 0; // 归零

  // 将PA1设置为输入模式,进行捕获
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  // 设置为上沿捕获
  __HAL_TIM_SET_CAPTUREPOLARITY(&htim5, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING);
  // 开启定时器捕获中断
  HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_2);
}

/*
重写捕获比较中断回调函数
*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  // 为了读数准确 关闭捕获定时器
  HAL_TIM_IC_Stop(&htim5, TIM_CHANNEL_2);

  // 读取值后存储到temp中
  temp = HAL_TIM_ReadCapturedValue(&htim5, TIM_CHANNEL_2);
  // flag置1,表示完成
  flag = 1;
}
/*
读取单次PA1函数
函数功能:读取PA1, 多次读取取平均值或最大值
参数:无
返回值:无
*/
void tpad_get_val(void)
{
  // 复位引脚函数
  tpad_reset();
  // 阻塞等待中断完成
  while (flag == 0)
  {
    HAL_Delay(1); // 防止刷新过快
  }
  // 完成后标志位置0
  flag = 0;
}

/*
读取最大PA1函数
函数功能:读取PA1, 多次读取取平均值或最大值
参数:读取次数
返回值:uint16_t
*/
uint16_t tpad_get_maxval(uint8_t i)
{
  uint16_t maxval = 0; // 存放最大值

  while (i--)
  {
    // 获取数据
    tpad_get_val();
    // 取最大值
    if (temp > maxval)
      maxval = temp;
  }
  return maxval;
}

/*
触摸按键初始化函数
函数功能:获取无接触的值
参数:无
返回值: 无
*/
void tpad_init(void)
{
  // 获取无接触值
  uint16_t buf[10];
  uint16_t m;
  uint8_t i, j;

  for (i = 0; i < 10; i++) // 获取10个数据
  {
    tpad_get_val();
    buf[i] = temp;
  }

  for (i = 0; i < 9; i++) // 进行排序
  {
    for (j = i + 1; j < 10; j++)
    {
      if (buf[i] < buf[j])
      {
        m = buf[i];
        buf[i] = buf[j];
        buf[j] = m;
      }
    }
  }
  m = 0;
  for (i = 2; i < 8; i++) // 取中间的6个数据
  {
    m += buf[i];
  }

  tpad_default_val = m / 6; // 求平均值作为没有触摸时的值
}

/**
 * @brief  扫描触摸按键
 * @param  mode : 扫描模式
 * @arg    0, 不支持连续触摸(按下一次必须松开才能按下一次)
 * @arg    1, 支持连续触发(可以一直按下)
 * @retval 0, 没有按下; 1, 有按下;
 */
uint8_t tpad_scan(uint8_t mode)
{
  uint8_t res = 0; // 返回值
  uint16_t rval = 0;
  uint8_t sample = 3;       /* 默认采样次数为 3 次 */
  static uint8_t keyen = 0; /* 0, 可以开始检测; >0, 还不能开始检测; */

  if (mode) // mode = 1 为扫描模式
  {
    sample = 6; // 支持连续按的时候,设置采样次数为 6 次
    keyen = 0;  // 支持连按,每次调用该函数都可以检测
  }
  // 获取PA1的值
  rval = tpad_get_maxval(sample); // 获取读取的值

  // 比较
  if (rval > (tpad_default_val + 15))
  {
    if (keyen == 0)
      res = 1; // 返回1代表有触摸
    keyen = 3;
  }
  if (keyen)
    keyen--;
  return res; // 返回0代表无触摸
}

阶段性mian文件

c 复制代码
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dht11.h"
#include "w25q128.h"
#include "oled.h"
#include "tpad.h"
/* USER CODE END Includes */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define MAXSIZE 256 // 数组长度
/* USER CODE END PD */


/* USER CODE BEGIN PV */
uint8_t uart_data = 0;               // 处理不定长参数
char uart_buf[MAXSIZE] = "";         // 保存不定长命令
uint16_t uart_flag = 0;              // 高位 字节用做标志位 低位 字节记录数组使用空间
uint16_t light_adc_dma_buf[2] = {0}; // 接收光敏数据
char msg[MAXSIZE] = "";              // 测试用
char write_data[MAXSIZE] = "";       // 写入w25q128闪存数据, 4字节数据位 + 数据
char read_data[MAXSIZE] = "";        // 读取w25q128闪存数据, 4字节数据位 + 数据
extern uint8_t dht11_data[5];
int dj_flag = 0; // 启动舵机标识 0未启动 1启动
/* USER CODE END PV */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void SSD1306_Init(void);
/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_ADC3_Init();
  MX_TIM1_Init();
  MX_SPI2_Init();
  MX_TIM2_Init();
  MX_I2C1_Init();
  MX_TIM5_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, &uart_data, 1);         // 处理不定长数据
  HAL_ADCEx_Calibration_Start(&hadc3);                 // 校准光敏
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); // w25q128 使用模式3 初始拉高片选引脚电压
  int times = 0;                                       // 写入频率
  int len = 0;
  SSD1306_Init(); // iic初始化显示文字
  tpad_init();    // 初始化tpad默认值
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (tpad_scan(0)) // 如果按下启动舵机
    {
      dj_flag = !dj_flag;
    }

    times++;
    len = 0;
    memset(msg, 0, sizeof(msg));
    if (DHT_read())
    {
      len = sprintf(msg, "sd=%d wd=%d ", dht11_data[0], dht11_data[2]);
    }
    HAL_ADC_Start_DMA(&hadc3, (uint32_t *)light_adc_dma_buf, 1); // dma模式获取光亮数值
    sprintf(msg + len, "light=%d", light_adc_dma_buf[0]);
    HAL_Delay(1000);

    // 每5秒写入一次
    if (times == 5)
    {
      times = 0;
      memset(write_data, 0, sizeof(write_data));
      sprintf(write_data, "%04d%s", strlen(msg), msg); // 组包写入闪存
      W25QXX_Write((uint8_t *)write_data, 0x00, strlen(write_data));

      HAL_Delay(500);

      memset(read_data, 0, sizeof(read_data));
      // 先读出4个字节获取数据长度
      W25QXX_Read((uint8_t *)read_data, 0x00, 4);
      len = atoi(read_data);
      W25QXX_Read((uint8_t *)read_data, 0x04, len);
      // 输出读取到的数据
      sprintf(msg, "read data: %s", read_data);
      HAL_UART_Transmit(&huart1, (uint8_t *)msg, strlen(msg), 1000);
      HAL_Delay(1000);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) // 重写回调函数
{
  if ((0x8000 & uart_flag) == 0) // 未收到\n结束
  {
    if (0x4000 & uart_flag) // 如果标志位当前是\r
    {
      // 判断本次字符是不是\r
      if (uart_data == '\n')
      {
        uart_flag |= 0x8000; // 如果收到\n更新标志位
      }
      else
      {
        uart_flag = 0; // \r后不是\n结束符不合法,重置数据
      }
    }
    else
    {
      if (uart_data == '\r') // 如果收到了\r更新标志位
      {
        uart_flag |= 0x4000;
      }
      else
      {
        // 正常数据存储到字符数组中
        uart_buf[uart_flag & 0x0fff] = uart_data;
        uart_flag++; // 下标偏移
      }
    }
  }

  if (0x8000 & uart_flag) // 收到完整的指令后
  {
    // 回显指令
    HAL_UART_Transmit(&huart1, (uint8_t *)uart_buf, uart_flag & 0x0fff, 1000);
    if (strncmp(uart_buf, "led:on", 6) == 0)
    {
      HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET);
    }
    else if (strncmp(uart_buf, "led:off", 7) == 0)
    {
      HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET);
    }
    else if (strncmp(uart_buf, "buzzer:on", 9) == 0) // 打开蜂鸣器
    {
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    }
    else if (strncmp(uart_buf, "buzzer:off", 10) == 0) // 关闭蜂鸣器
    {
      HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
    }
    uart_flag = 0; // 处理完命令清空数据
    memset(uart_buf, 0, sizeof(uart_buf));
  }

  HAL_UART_Receive_IT(&huart1, &uart_data, 1); // 重新开启中断接收
}

void SSD1306_Init(void)
{
  OLED_Init(); // 初始化oled
  // 显示默认字样
  OLED_ShowCN(0, 0, 0);                   // 速
  OLED_ShowCN(16, 0, 1);                  // 度
  OLED_ShowStr(32, 0, (uint8_t *)":", 2); // :
  OLED_ShowStr(64, 0, (uint8_t *)"0", 2); // 0

  OLED_ShowCN(0, 2, 2);                   // 光
  OLED_ShowCN(16, 2, 3);                  // 照
  OLED_ShowStr(32, 2, (uint8_t *)":", 2); // :
  OLED_ShowStr(64, 2, (uint8_t *)"0", 2); // 0

  OLED_ShowCN(0, 4, 5);                   // 温
  OLED_ShowCN(16, 4, 6);                  // 度
  OLED_ShowStr(32, 4, (uint8_t *)":", 2); // :
  OLED_ShowStr(64, 4, (uint8_t *)"0", 2); // 0

  OLED_ShowCN(0, 6, 4);                   // 湿
  OLED_ShowCN(16, 6, 6);                  // 度
  OLED_ShowStr(32, 6, (uint8_t *)":", 2); // :
  OLED_ShowStr(64, 6, (uint8_t *)"0", 2); // 0
}
/* USER CODE END 4 */
相关推荐
来自晴朗的明天6 小时前
16、电压跟随器(缓冲器)电路
单片机·嵌入式硬件·硬件工程
钰珠AIOT6 小时前
在同一块电路板上同时存在 0805 0603 不同的封装有什么利弊?
嵌入式硬件
代码游侠6 小时前
复习——Linux设备驱动开发笔记
linux·arm开发·驱动开发·笔记·嵌入式硬件·架构
代码游侠17 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg200519 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT21 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen21 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
全栈游侠1 天前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件
Lsir10110_1 天前
【Linux】中断 —— 操作系统的运行基石
linux·运维·嵌入式硬件