目录
STM32作业设计
STM32作业实现(一)串口通信
STM32作业实现(二)串口控制led
STM32作业实现(三)串口控制有源蜂鸣器
STM32作业实现(四)光敏传感器
STM32作业实现(五)温湿度传感器dht11
STM32作业实现(六)闪存保存数据
STM32作业实现(七)OLED显示数据
STM32作业实现(八)触摸按键TPAD
STM32作业实现(九)驱动舵机
源码位置
编写Tpad驱动文件(控制舵机开关)
开启所需引脚
开启中断模式
tpad.h
#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
#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文件
/* 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 */