CW32030C8T6饭盒派学习笔记
前言
参考文章\资源
CW Programmer CW32系列芯片的烧录工具配套软件
吐槽一下,CW32是真的难搞,网上资料也不多,最好去看固件库例程跟着搞吧
开箱
这个板子是参加圆梦杯申请的,饭盒派CW32_48F/L大学计划开发板
焊接前
一块
CW32030C8T6最小系统板,然后还给了三片CW32L031C8(超低功耗MCU产品) 样片
焊接后
注意:电容是有极性的,灰色区域是负极(引脚长为正短为负),对应板子上有+号的是正极,蜂鸣器也是引脚长为正短为负,也可以看丝印有+号,对应板子也上有+号,晶振不分正负,但是焊接要小心不能连锡了
配置开发环境
- 去官网下载最新的固件库,解压即可,然后找到pack包(在压缩包里),直接双击安装即可
- 新建工程
正常新建即可,选择芯片那就选对应的型号,然后这里需要勾选 CORE
- 然后在工程文件夹里新建3个文件夹,
app---存放外设,USER---存放主函数代码还有公用代码,LIB---存放CW库.c.h,OTHER---存放启动文件,中断服务程序文件等,把刚刚下载解压的固件库里的对应文件复制过去
core_cm0plus.h在路径:C:\Users\44478\AppData\Local\Arm\Packs\ARM\CMSIS\5.8.0\CMSIS\Core\Include下可以找到
| 工程文件夹 | 存放 |
|---|---|
| USER | main.c,main.h(主函数)public.c,public.h (公用函数)system.c,system.h (系统函数)system_init.c,system_init.h (系统初始函数)callback.c,callback.h (中断函数)task.c,task.h |
| LIB | inc文件夹 src文件夹 |
| APP | 外设代码 |
| OTHER | startup_cw32f030.s core_cm0plus.h |
- Wch-link接线
需要注意要给TFT屏幕接5V,或者加多条线给最小系统板USB供电,否则屏幕可能很暗
如果没有link也可以进行ISP下载就是通过串口1下载hex文件,去下载
CW32_Pragrammer软件进行下载要进入ISP烧录模式需要将BOOT引脚上拉后再通电
- 然后一般头文件包含这3个,后面用到库再包含对应库头文件
#include "base_types.h"
#include "cw32f030.h"
#include "system_cw32f030.h"- 搞好好直接vscode进行写代码烧写即可
- 最终的固定代码
system_init.h
#ifndef __SYSTEM_INIT_H
#define __SYSTEM_INIT_H
#include "main.h"
typedef struct
{
void (*Hardware_Init)(void); // 硬件初始化
}System_Init_t;
extern System_Init_t System_Init;
#endifsystem_init.c
/***************************************************************************
* File: system_init.c
* Author: Luckys.
* Date: 2023/06/11
* description: 系统初始化
****************************************************************************/
#include "main.h"
/*====================================variable definition declaration area BEGIN===================================*/
uint8_t Init_Cnt = 10; // 初始化超时等待计数
/*====================================variable definition declaration area END===================================*/
/*====================================static function declaration area BEGIN====================================*/
static void Hardware_Init(void); // 硬件初始化
static void RCC_Config(void); // 时钟配置
/*====================================static function declaration area END====================================*/
System_Init_t System_Init =
{
Hardware_Init,
};
/*
* @function: Hardware_Init
* @param: None
* @retval: None
* @brief: 硬件初始化
*/
static void Hardware_Init(void)
{
Public.System_MS_Delay(1000); // DHT11上电后至少需要延时1s等待稳定
RCC_Config(); // 时钟配置
InitTick(64000000); // SYSTICK初始化
Btim.BTIM1_Init(1999, BTIM_PRS_DIV32); // 基本定时器1初始化(计算:64000000 / 2000 / 32 = 1000Hz --- 1 / 1000Hz = 0.001s)
Led.Led_Init(); // LED初始化
Key_1.Key_1_Init(); // 按键初始化
Key_2.Key_2_Init(); // 按键初始化
Buzzer.Buzzer_Init(); // 蜂鸣器初始化
#ifdef USE_TFT
ST7735.ST7735_Init(); // ST7735 LCD屏幕初始化
#elif defined(USE_096_OLED)
I2C_Soft.I2C_Init(); // I2C初始化
OLED096.OLED096_Init(); // 0.96寸OLED初始化
#elif defined(USE_091_OLED)
// 0.91寸OLED代码待添加
#endif
#ifdef USE_ADC_Single_One
ADC_1.ADC1_Single_Channel_One_Init(); // ADC单通道单次采集初始化
#elif defined(USE_ADC_Serial_Scan)
ADC_1.ADC1_Serial_Scan_Init(); // ADC序列扫描初始化
#endif
USART1.USART1_Init(); // 串口1初始化
myRTC.myRTC_Init(2023, RTC_Month_June, 16, RTC_Weekday_Friday, 18, 50, 20); // RTC初始化
myRTC.myRTC_Alarm_A_Init(); // 闹钟初始化
while (!DHT11.DHT11_Init() && (Init_Cnt--)) // DHT11初始化
{
Public.System_MS_Delay(100);
}
DHT11.DHT11_Read_Data(&DHT11.DHT11_Temperture,&DHT11.DHT11_Humidity); // 获取一次温湿度
Gtim.Gtim1_PWM_Output_Init(399, GTIM_PRESCALER_DIV64); // PWM输出初始化 64000000 / 64 / 400 = 2500Hz (即2.5ms)
#ifdef USE_PWM_IC
Gtim.Gtim2_PWM_IC_Init(); // 输入捕获初始化
#endif
#ifdef USE_GTIM3_TOGG
Gtim.Gtim3_PWM_Toggle_Init(399, GTIM_PRESCALER_DIV64); // 互补初始化 64000000 / 1024 / 12500 = 5hz (即200ms)
#endif
// Atim.Atim_Base_Init(1999, ATIM_Prescaler_DIV32); // 高级定时器1初始化(计算:64000000 / 2000 / 32 = 1000Hz --- 1 / 1000Hz = 0.001s)
#ifdef USE_PWM_IC_ATIM
Atim.Atim_PWM_Input_Init();
#endif
#ifdef USE_ATIM_PWM
Atim.Atim_PWM_Output_OC_Init();
#endif
printf("初始化完成\r\n");
}
/*
* @function: RCC_Config
* @param: None
* @retval: None
* @brief: 时钟配置
*/
static void RCC_Config(void)
{
/* 0. HSI使能并校准 */
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
/* 1. 设置HCLK和PCLK的分频系数 */
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
/* 2. 使能PLL,通过PLL倍频到64MHz */
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8); // HSI 默认输出频率8MHz
// RCC_PLL_OUT(); //PC13脚输出PLL时钟
///< 当使用的时钟源HCLK大于24M,小于等于48MHz:设置FLASH 读等待周期为2 cycle
///< 当使用的时钟源HCLK大于48MHz:设置FLASH 读等待周期为3 cycle
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_3);
/* 3. 时钟切换到PLL */
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(64000000);
}
system.h
#ifndef __SYSTEM_H
#define __SYSTEM_H
#include "main.h"
typedef struct
{
void (*System_Run)(void); // 系统运行
void (*Error_Handler)(void); // 系统错误处理
void (*Task_Marks_Handler)(void); // 任务标记
}System_t;
extern System_t System;
#endifsystem.c
/***************************************************************************
* File: system.c
* Author: Luckys.
* Date: 2023/06/11
* description: 系统函数
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void System_Run(void); // 系统运行
static void Error_Handler(void); // 系统错误处理
static void Task_Marks_Handler(void); // 任务标记函数
static void Task_Pro_Handler(void); // 任务处理函数
/*====================================static function declaration area END====================================*/
System_t System =
{
System_Run,
Error_Handler,
Task_Marks_Handler,
};
/*
* @function: System_Run
* @param: None
* @retval: None
* @brief: 系统运行
*/
static void System_Run(void)
{
Task_Pro_Handler();
}
/*
* @function: Error_Handler
* @param: None
* @retval: None
* @brief: 系统错误处理
*/
static void Error_Handler(void)
{
Buzzer.Buzzer_ON();
}
/*
* @function: Task_Marks_Handler
* @param: None
* @retval: None
* @brief: 任务标记函数
*/
static void Task_Marks_Handler(void)
{
uint8_t i;
for (i = 0; i < ucTasks_Max; i++)
{
if (Task[i].Task_Cnt) // 判断计数是否为0
{
Task[i].Task_Cnt--; // 递减
if (0 == Task[i].Task_Cnt) // 计数到0
{
Task[i].Task_Cnt = Task[i].Task_Timer; // 重装载计数
Task[i].Run_Status = TRUE; // 任务执行状态标志置1
}
}
}
}
/*
* @function: Task_Pro_Handler
* @param: None
* @retval: None
* @brief: 任务处理函数
*/
static void Task_Pro_Handler(void)
{
uint8_t i;
for (i = 0; i < ucTasks_Max; i++)
{
if (Task[i].Run_Status) // 判断执行状态:TRUE--执行 FALSE--不执行
{
Task[i].Run_Status = FALSE;
Task[i].Task_Hook(); // 执行函数
}
}
}
public.h
#ifndef __PUBLIC_H
#define __PUBLIC_H
#include "main.h"
// Debug Port(Default serial port 1)
#define UART_DEBUG CW_UART1
/***********************全局宏预编译 BEGIN***********************/
// 选择屏幕(只能3选1)
// #define USE_TFT
#define USE_096_OLED
// #define USE_091_OLED
// ADC选择模式(单通道单次/序列扫描)
// #define USE_ADC_Single_One
#define USE_ADC_Serial_Scan
// RTC中断选择打开
// #define USE_RTC_Interrupt
// 输入捕获有问题,暂时屏蔽
// #define USE_PWM_IC
// #define USE_PWM_IC_ATIM
// GTIM3互补输出
// #define USE_GTIM3_TOGG
// ATIM输出比较
// #define USE_ATIM_PWM
/***********************全局宏预编译 END***********************/
// 取消 FALSE 和 TRUE 宏定义(否则下面枚举报错!)
#undef FALSE
#undef TRUE
// BIT
typedef enum
{
BIT0 = (uint8_t)(0x01 << 0), // 0x01 -- 0000 0001
BIT1 = (uint8_t)(0x01 << 1), // 0x02 -- 0000 0010
BIT2 = (uint8_t)(0x01 << 2), // 0x04 -- 0000 0100
BIT3 = (uint8_t)(0x01 << 3), // 0x08 -- 0000 1000
BIT4 = (uint8_t)(0x01 << 4), // 0x10 -- 0001 0000
BIT5 = (uint8_t)(0x01 << 5), // 0x20 -- 0010 0000
BIT6 = (uint8_t)(0x01 << 6), // 0x40 -- 0100 0000
BIT7 = (uint8_t)(0x01 << 7), // 0x80 -- 1000 0000
}BIT_t;
// TRUE/FALSE
typedef enum
{
FALSE = 0U,
TRUE = !FALSE
}FLagStatus_t;
// 超时时间
typedef enum
{
UART_TX_TimerOut = (uint8_t)100, // 串口发送单字节等待最大时间(ms)
}TIMER_OUT_t;
typedef struct
{
void (*System_10US_Delay)(uint32_t); // 系统延时10*xus
void (*System_MS_Delay)(uint32_t); // 系统ms延时
void (*Memory_Clear)(uint8_t*, uint16_t); // 内存清除
}Public_t;
extern Public_t Public;
#endif
public.c
/***************************************************************************
* File: USER
* Author: Luckys.
* Date: 2023/06/11
* description: 公用部分代码
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void Memory_Clear(uint8_t*, uint16_t); // 内存清除
static void System_MS_Delay(uint32_t); // 系统ms延时
static void System_10US_Delay(uint32_t); // 系统延时10*xus
/*====================================static function declaration area END====================================*/
Public_t Public =
{
System_10US_Delay,
System_MS_Delay,
Memory_Clear,
};
/*
* @function: Memory_Clear
* @param: puc_Buffer -> 要清除的内存首地址 LEN -> 内存长度(注意字串符用strlen() 数组用sizeof() !!!)
* @retval: None
* @brief: 内存清除
*/
static void Memory_Clear(uint8_t* puc_Buffer, uint16_t LEN)
{
uint16_t i = 0;
for (i = 0; i < LEN; i++)
{
*(puc_Buffer + i) = (uint8_t)0;
}
}
/*
* @function: fputc
* @param: ch -> 要写入的字符的 ASCII 码值,应当是一个整型数 f -> 指向要写入的文件(或流)的指针
* @retval: None
* @brief: 重定向printf
*/
int fputc(int ch, FILE *f)
{
USART_SendData_8bit(UART_DEBUG, (uint8_t)ch); // 发送一个数据(8bit)
// 等待发送完成,1:完成,0:还没完成
while (USART_GetFlagStatus(UART_DEBUG, USART_FLAG_TXE) == RESET);
return ch;
}
/*
* @function: System_MS_Delay
* @param: ms -> 需要延时的时间(ms)
* @retval: None
* @brief: 系统ms延时
*/
static void System_MS_Delay(uint32_t ms)
{
delay1ms(ms); // CW库的延时函数
}
/*
* @function: System_10US_Delay
* @param: us -> 需要延时的时间(us*10)
* @retval: None
* @brief: 系统10*xus延时
*/
static void System_10US_Delay(uint32_t us)
{
delay10us(us); // CW库的延时函数
}
callback.h
#ifndef __CALLBACK_H
#define __CALLBACK_H
#include "main.h"
// 调试用
#define CALLBACK_Debug 0
#endifcallback.c
/***************************************************************************
* File: USER
* Author: Luckys.
* Date: 2023/06/11
* description: 中断函数
****************************************************************************/
#include "main.h"
/*
* @function: UART1_IRQHandler
* @param: None
* @retval: None
* @brief: 串口1中断服务函数
*/
void UART1_IRQHandler(void)
{
if (USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
{
USART1.puc_Usart1_Rec_Buffer[USART1.ucUsart1_Rx_Cnt] = USART_ReceiveData_8bit(CW_UART1);
if (USART1.puc_Usart1_Rec_Buffer[USART1.ucUsart1_Rx_Cnt] == 0x0A)
{
if (USART1.puc_Usart1_Rec_Buffer[USART1.ucUsart1_Rx_Cnt - 1] == 0x0D) // 判断先后接收到 0x0D 0x0A 则当做一帧
{
#if CALLBACK_Debug
// for (uint16_t i = 0; i < USART1.ucUsart1_Rx_Cnt - 1; i++) // Cnt - 1去除0x0D
// {
// *(USART1.puc_Usart1_Send_Buffer + i) = *(USART1.puc_Usart1_Rec_Buffer + i);
// }
// // 发送数据
// USART1.USART1_Send_Array(USART1.puc_Usart1_Send_Buffer, USART1.ucUsart1_Rx_Cnt - 1); // Cnt - 1去除0x0D
// printf("\r\n");
#endif
// ModBus协议解析
Modbus.Protocol_Analysis(&USART1);
}
else
{
USART1.ucUsart1_Rx_Cnt++;
}
}
else
{
USART1.ucUsart1_Rx_Cnt++;
}
USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
}
}
/*
* @function: BTIM1_IRQHandler
* @param: None
* @retval: None
* @brief: BTIM1中断服务函数
*/
void BTIM1_IRQHandler(void)
{
static uint16_t count_100ms = 0;
if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV)) //检查BTIM的状态寄存器的状态位是否置位
{
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV); //清除中断标志位
count_100ms++;
System.Task_Marks_Handler();
if(100 == count_100ms)
{
count_100ms = 0;
Key_1.vusKey_1_Timer_Count++;
}
}
}
/*
* @function: GPIOA_IRQHandler
* @param: None
* @retval: None
* @brief: GPIOB外部中断服务函数
*/
void GPIOB_IRQHandler(void)
{
if (CW_GPIOB->ISR_f.PIN2) // 判断哪个引脚触发
{
GPIOB_INTFLAG_CLR(bv2); // 清除标志位
Led.Led_Flip(LED2);
}
}
/*
* @function: ADC_IRQHandler
* @param: None
* @retval: None
* @brief: ADC中断服务函数
*/
void ADC_IRQHandler(void)
{
ADC_1.gFlagIrq = CW_ADC->ISR; // 获取中断标志寄存器
CW_ADC->ICR = 0x00; // 中断标志清除寄存器
}
/*
* @function: RTC_IRQHandler
* @param: None
* @retval: None
* @brief: RTC中断服务函数
*/
void RTC_IRQHandler(void)
{
if (RTC_GetITState(RTC_IT_ALARMA)) // 闹钟中断触发
{
RTC_ClearITPendingBit(RTC_IT_ALARMA);
Buzzer.Buzzer_ON();
}
#ifdef USE_RTC_Interrupt
if (RTC_GetITState(RTC_IT_INTERVAL)) // RTC秒中断触发
{
RTC_ClearITPendingBit(RTC_IT_INTERVAL);
}
#endif
}
/*
* @function: GTIM1_IRQHandler
* @param: None
* @retval: None
* @brief: 定时器1中断函数
*/
void GTIM1_IRQHandler(void)
{
// static uint16_t Timer_Cnt;
GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
// 需要再打开
// Timer_Cnt++;
// if (Timer_Cnt >= 800) // 2.5x400 = 2s
// {
// Timer_Cnt = 0;
// Gtim.Gtim1_CH_Set_Pulse[0] += 40; // 占空比+10%
// if (Gtim.Gtim1_CH_Set_Pulse[0] > 360)
// {
// Gtim.Gtim1_CH_Set_Pulse[0] = 40;
// }
// // GTIM_SetCompare1(CW_GTIM1, Gtim.Gtim1_CH_Set_Pulse[3]);
// CW_GTIM1->CCR1 = Gtim.Gtim1_CH_Set_Pulse[3]; // 设置占空比跟上面函数作用一样
// }
}
/*
* @function: ATIM_IRQHandler
* @param: None
* @retval: None
* @brief: 高级定时器中断服务函数
*/
void ATIM_IRQHandler(void)
{
if (ATIM_GetITStatus(ATIM_IT_OVF))
{
ATIM_ClearITPendingBit(ATIM_IT_OVF);
}
if (ATIM_GetITStatus(ATIM_IT_C1BF))
{
ATIM_ClearITPendingBit(ATIM_IT_C1BF);
}
if (ATIM_GetITStatus(ATIM_IT_C1AF))
{
ATIM_ClearITPendingBit(ATIM_IT_C1AF);
}
}
#ifdef USE_PWM_IC
/*
* @function: GTIM2_IRQHandler
* @param: None
* @retval: None
* @brief: GTIM2中断服务函数
*/
void GTIM2_IRQHandler(void)
{
static uint8_t Status = 0; // 标志位,用于表示当前处于PWM信号的哪一阶段
static uint32_t cnt = 0; // 计数
if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_OV)) // 判断是否为GTIM1计数器溢出中断
{
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_OV); // 清除计数器溢出中断标志位
if (Status == 1) // 如果当前处于PWM信号的第二阶段
{
cnt++;
}
}
if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC1)) // 判断是否为GTIM1捕获比较匹配中断
{
if (Status == 0) // 如果当前处于PWM信号的第一阶段
{
Gtim.Gtim2_IC_Fre = CW_GTIM2->CCR1; // 获取捕获比较器1的值,即为PWM信号的周期
Status = 1; // 切换至PWM信号的第二阶段
}
else if (Status == 1) // 如果当前处于PWM信号的第二阶段
{
Gtim.Gtim2_IC_Fre = CW_GTIM2->CCR1 + cnt * 65536 - Gtim.Gtim2_IC_Fre; // 计算PWM信号的周期
Status = 0; // 切换至PWM信号的第一阶段
cnt = 0; // 计数器清零
}
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC1); // 清除捕获比较器1匹配中断标志位
}
if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC2)) // 判断是否为GTIM1捕获比较匹配中断
{
if (Status == 1) // 如果当前处于PWM信号的第二阶段
{
Gtim.Gtim2_IC_Duty = CW_GTIM2->CCR2 + cnt * 65536 - Gtim.Gtim2_IC_Fre; // 计算PWM信号的占空比
}
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC2); // 清除捕获比较器2匹配中断标志位
}
}
#endif
task.h
#ifndef __TASK_H
#define __TASK_H
#include "main.h"
typedef struct
{
uint8_t Run_Status; // 任务状态:TRUE/FALSE
uint16_t Task_Cnt; // 任务定时计数器(ms)
uint16_t Task_Timer; // 重载计数器(任务分配的时间ms)
void (*Task_Hook)(void); // 任务函数
}Task_t;
extern Task_t Task[];
extern uint8_t ucTasks_Max;
#endiftask.c
/***************************************************************************
* File: task.c
* Author: Luckys.
* Date: 2023/06/13
* description: 任务调度
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void TasksHandle_10MS(void); // 任务
static void TasksHandle_20MS(void); // 任务
static void TasksHandle_100MS(void); // 任务
static void TasksHandle_250MS(void); // 任务
static void TasksHandle_1S(void); // 任务
static void TasksHandle_1p5S(void); // 任务
/*====================================static function declaration area END====================================*/
Task_t Task[] =
{
{FALSE, 10, 10, TasksHandle_10MS}, // task Period: 10ms
{FALSE, 100, 100, TasksHandle_100MS}, // task Period: 20ms
{FALSE, 20, 20, TasksHandle_20MS}, // task Period: 20ms
{FALSE, 250, 250, TasksHandle_250MS}, // task Period: 250ms
{FALSE, 1000, 1000, TasksHandle_1S}, // task Period: 1s
{FALSE, 1500, 1500, TasksHandle_1p5S}, // task Period: 1.5s
};
/*====================================variable definition declaration area BEGIN===================================*/
// 最大任务数量
uint8_t ucTasks_Max = sizeof(Task) / sizeof(Task[0]);
/*====================================variable definition declaration area END===================================*/
/*
* @function: TasksHandle_10MS
* @param: None
* @retval: None
* @brief: 任务
*/
static void TasksHandle_10MS(void)
{
Key_1.Key_1_Scan();
Key_1.Key_1_Handler();
}
/*
* @function: TasksHandle_20MS
* @param: None
* @retval: None
* @brief: 任务
*/
static void TasksHandle_20MS(void)
{
#ifdef USE_096_OLED
switch(Menu.Now_Page_Status)
{
case PAGE_TempHum:Menu.Menu_Page1();break;
case PAGE_RtcTimeDate:Menu.Menu_Page2();break;
case PAGE_ADC:Menu.Menu_Page3();break;
case PAGE_PWM:Menu.Menu_Page4();break;
default:Menu.Now_Page_Status = PAGE_TempHum;break;
}
#endif
}
/*
* @function: TasksHandle_100MS
* @param: None
* @retval: None
* @brief: 任务
*/
static void TasksHandle_100MS(void)
{
#ifdef USE_ADC_Single_One
ADC_1.ADC1_Single_Channel_One_Convert();
#elif defined(USE_ADC_Serial_Scan)
ADC_1.ADC1_Serial_Scan_Convert();
#endif
if (Menu.Now_Page_Status == PAGE_ADC)
{
// OLED刷新
// printf("A---%.1f\r\n",ADC_1.ADC_Single_Result);
sprintf((char*)Page3.OLED096_Display_Buff[0],"B0:%.2f A4:%.2f",ADC_1.ADC_Serial_Result_Arr[0], ADC_1.ADC_Serial_Result_Arr[1]);
OLED096.padString((char*)Page3.OLED096_Display_Buff[0],16);
sprintf((char*)Page3.OLED096_Display_Buff[1],"A5:%.2f A6:%.2f",ADC_1.ADC_Serial_Result_Arr[2], ADC_1.ADC_Serial_Result_Arr[3]);
OLED096.padString((char*)Page3.OLED096_Display_Buff[1],16);
}
}
/*
* @function: TasksHandle_250MS
* @param: None
* @retval: None
* @brief: 任务
*/
static void TasksHandle_250MS(void)
{
Led.Led_Flip(LED1);
if (Menu.Now_Page_Status == PAGE_PWM)
{
Gtim.Gtim1_Calculate(); // 计算
}
}
/*
* @function: TasksHandle_1S
* @param: None
* @retval: None
* @brief: 任务
*/
static void TasksHandle_1S(void)
{
Led.Led_Flip(LED2);
if (Menu.Now_Page_Status == PAGE_RtcTimeDate)
{
myRTC.myRTC_Refresh();
}
}
/*
* @function: TasksHandle_1p5S
* @param: None
* @retval: None
* @brief: 任务
*/
static void TasksHandle_1p5S(void)
{
if (Menu.Now_Page_Status == PAGE_TempHum)
{
DHT11.DHT11_Read_Data(&DHT11.DHT11_Temperture,&DHT11.DHT11_Humidity); // 获取一次温湿度
}
}
main.h
#ifndef __MAIN_H
#define __MAIN_H
#include "base_types.h"
#include "cw32f030.h"
#include "system_cw32f030.h"
#include "cw32f030_adc.h"
#include "cw32f030_atim.h"
#include "cw32f030_awt.h"
#include "cw32f030_btim.h"
#include "cw32f030_crc.h"
#include "cw32f030_debug.h"
#include "cw32f030_digitalsign.h"
#include "cw32f030_dma.h"
#include "cw32f030_flash.h"
#include "cw32f030_gpio.h"
#include "cw32f030_gtim.h"
#include "cw32f030_i2c.h"
#include "cw32f030_iwdt.h"
#include "cw32f030_lvd.h"
#include "cw32f030_pwr.h"
#include "cw32f030_ram.h"
#include "cw32f030_rcc.h"
#include "cw32f030_rtc.h"
#include "cw32f030_spi.h"
#include "cw32f030_systick.h"
#include "cw32f030_uart.h"
#include "cw32f030_wwdt.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "task.h"
#include "system_init.h"
#include "system.h"
#include "callback.h"
#include "public.h"
#include "led.h"
#include "key_1.h"
#include "key_2.h"
#include "buzzer.h"
#include "usart1.h"
#include "crc_16.h"
#include "modbus.h"
#include "btim.h"
#include "gtim.h"
#include "atim.h"
#include "tft_st7735.h"
#include "i2c.h"
#include "oled_096.h"
#include "adc1.h"
#include "rtc.h"
#include "menu.h"
#include "dht11.h"
#endif
main.c
/***************************************************************************
* File: main.c
* Author: Luckys.
* Date: 2023/06/11
* description: CW32030C8T6大学板
****************************************************************************/
#include "main.h"
int main(void)
{
System_Init.Hardware_Init();
while (1)
{
System.System_Run();
}
}板子资源
- 资源外设
48PINMCU: CW32F030C8T6位微控制器,64M主频 ,LQFP48封装4针SWD下载仿真接口;
一个DC口,开发板可
12V电源接口;5V、GND、3.3V电源,通过
2.54mm的单排针孔位引出;所有GPIO通过
2.54mm(100mil)间距双排针孔位引出;一个系统复位按键
电源LED指示灯;
3个用户按键;
3个LED灯
一个1.77TFT显示屏;
一个有源蜂鸣器;
一路电位器;
蓝牙接口;
WIFI接口;
矩阵键盘接口;
电子秤接口;
MPU6050接口;
DHT11温湿度接口;
数据手册阅读
- 内核:ARM® Cortex®-M0+,最高主频 64MHz
- 工作电压:1.65V 至 5.5V
- 最大 64K 字节 FLASH,最大 8K 字节 RAM,128 字节 OTP 存储器
- 一组高级控制PWM 定时器,四组 16 位通用定时器,三组 16 位基本定时器,窗口看门狗定时器,独立看门狗定时器
- 三路低功耗 UART
- 两路 SPI 接口 12Mbit/s
- 两路 I2C 接口 1Mbit/s
- 12 位ADC 一个
- 80 位唯一ID
- 框图
- 时钟树
HSIOSC 时钟频率固定为48MHz,频率精度低于HSE 时钟
系统上电复位完成后默认选择HSI 作为SysClk 的时钟源,时钟频率默认值是8MHz
所以我们可以HSIOSC进行6分频变成 8MHz,然后通过PLL进行8倍频变成 64MHz,这样系统时钟就设置为最大了,然后SYSCLK进行1分频,HCLK频率等于系统时钟64MHz
static void RCC_Config(void)
{
/* 0. HSI使能并校准 */
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
/* 1. 设置HCLK和PCLK的分频系数 */
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
/* 2. 使能PLL,通过PLL倍频到64MHz */
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8); // HSI 默认输出频率8MHz
// RCC_PLL_OUT(); //PC13脚输出PLL时钟
///< 当使用的时钟源HCLK大于24M,小于等于48MHz:设置FLASH 读等待周期为2 cycle
///< 当使用的时钟源HCLK大于48MHz:设置FLASH 读等待周期为3 cycle
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_3);
/* 3. 时钟切换到PLL */
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(64000000);
}手册问题
cw32f030_gpio.h
库函数跟STM32F103固件库一模一样的用法,那些函数都是同样的
但是多了一些简化操作(以PA引脚为例,其他的一样操作),x用数字代替!
// 设置PAx引脚为高电平
PA0x_SETHIGH();
// 设置PAx引脚为低电平
PA0x_SETLOW();
// 翻转PAx引脚状态
PA0x_TOG();
// 复用PA14为I2C
PA14_AFx_GPIO();
PA14_AFx_I2C1SCL();
// 设置为推挽输出或者开漏
PA0x_PUSHPULL_ENABLE();
PA0x_OPENDRAIN_ENABLE();
// 设置为输入输出
PA0x_DIR_OUTPUT();
PA0x_DIR_INPUT();
// 设置为模拟输入
PA0x_ANALOG_ENABLE();
// 设置为数字
PA0x_DIGTAL_ENABLE();- 复用的话查手册GPIO那
- 端口复位状态
上电或复位后, SWCLK(PA14) 和 SWDIO (PA13) 默认为数字上拉, BOOT (PF3) 默认为数字功能。其他端口默认为模拟高阻输入,上拉或下拉均默认不打开。
- 使能时钟的函数可以是这样
RCC_APBPeriphClk_Enable2(RCC_AHB_PERIPH_GPIOA,ENABLE); // 跟STM32一样
// 另一种是直接点(CW特有的)
__RCC_GPIOA_CLK_ENABLE();- 中断通道号可以在
cw32f030.h里找到,初始化可能用得到(如果形参类型是IRQn_Type IRQn就是通道号)
SYStick
cw32f030_systick.c
滴答定时器,默认1ms,需要用户启动
InitTick(64000000); // 参数是HCLK时钟频率常用函数:
// ms延时
SysTickDelay(uint32_t Delay)
// 获取当前计数值
uint32_t GetTick(void) 中断服务函数
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn */
uwTick += uwTickFreq;
/* USER CODE END SysTick_IRQn */
}滴答定时器准确度的话应该不错的用示波器测量过,延时100ms翻转一次电平,测量的引脚频率是差不多5Hz也就是周期200ms
在
system_cw32f030.c里一些系统函数,比如delay1ms,delay10us
LED
- 硬件连接
- 程序编写
system_init.c
/*
* @function: Hardware_Init
* @param: None
* @retval: None
* @brief: 硬件初始化
*/
static void Hardware_Init(void)
{
RCC_Config(); // 时钟配置
InitTick(64000000); // SYSTICK初始化
Led.Led_Init(); // LED初始化
}led.h
#ifndef __LED_H
#define __LED_H
#include "main.h"
// 管脚 LED1--PA7 LED2--PA8 LED--PC13
#define Led1_Pin GPIO_PIN_7
#define Led2_Pin GPIO_PIN_8
#define Led3_Pin GPIO_PIN_13
// 定义枚举类型
typedef enum
{
LED1 = (uint8_t)0x01,
LED2 = (uint8_t)0x02,
LED3 = (uint8_t)0x03,
} Led_Num_t;
typedef struct
{
void (*Led_Init)(void); // LED初始化
void (*Led_ON)(Led_Num_t); // 打开
void (*Led_OFF)(Led_Num_t); // 关闭
void (*Led_Flip)(Led_Num_t); // 翻转
} Led_t;
extern Led_t Led;
#endifled.c
/***************************************************************************
* File: led.c
* Author: Luckys.
* Date: 2023/06/11
* description: LED
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void Led_Init(void); // LED初始化
static void Led_ON(Led_Num_t); // 打开
static void Led_OFF(Led_Num_t); // 关闭
static void Led_Flip(Led_Num_t); // 翻转
/*====================================static function declaration area END====================================*/
Led_t Led =
{
Led_Init,
Led_ON,
Led_OFF,
Led_Flip,
};
/*
* @function: Led_Init
* @param: None
* @retval: None
* @brief: LED初始化
*/
static void Led_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能时钟
__RCC_GPIOC_CLK_ENABLE();
__RCC_GPIOA_CLK_ENABLE();
GPIO_InitStructure.IT = GPIO_IT_NONE; // 管脚中断模式--无
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 模式
GPIO_InitStructure.Pins = Led1_Pin | Led2_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.Pins = Led3_Pin;
GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
GPIO_WritePin(CW_GPIOA, Led1_Pin | Led2_Pin, GPIO_Pin_SET); // 默认灭
GPIO_WritePin(CW_GPIOC, Led3_Pin, GPIO_Pin_SET);
}
/*
* @function: Led_ON
* @param: LEDx -> 1,2,3
* @retval: None
* @brief: LED打开
*/
static void Led_ON(Led_Num_t LEDx)
{
switch(LEDx)
{
case LED1:
{
GPIO_WritePin(CW_GPIOA, Led1_Pin, GPIO_Pin_RESET);
break;
}
case LED2:
{
GPIO_WritePin(CW_GPIOA, Led2_Pin, GPIO_Pin_RESET);
break;
}
case LED3:
{
GPIO_WritePin(CW_GPIOC, Led3_Pin, GPIO_Pin_RESET);
break;
}
default:
{
GPIO_WritePin(CW_GPIOA, Led1_Pin, GPIO_Pin_RESET);
break;
}
}
}
/*
* @function: Led_OFF
* @param: LEDx -> 1,2,3
* @retval: None
* @brief: LED关闭
*/
static void Led_OFF(Led_Num_t LEDx)
{
switch(LEDx)
{
case LED1:
{
GPIO_WritePin(CW_GPIOA, Led1_Pin, GPIO_Pin_SET);
break;
}
case LED2:
{
GPIO_WritePin(CW_GPIOA, Led2_Pin, GPIO_Pin_SET);
break;
}
case LED3:
{
GPIO_WritePin(CW_GPIOC, Led3_Pin, GPIO_Pin_SET);
break;
}
default:
{
GPIO_WritePin(CW_GPIOA, Led1_Pin, GPIO_Pin_SET);
break;
}
}
}
/*
* @function: Led_Flip
* @param: LEDx -> 1,2,3
* @retval: None
* @brief: LED初始化
*/
static void Led_Flip(Led_Num_t LEDx)
{
switch(LEDx)
{
case LED1:
{
GPIO_TogglePin(CW_GPIOA, Led1_Pin);
break;
}
case LED2:
{
GPIO_TogglePin(CW_GPIOA, Led2_Pin);
break;
}
case LED3:
{
GPIO_TogglePin(CW_GPIOC, Led3_Pin);
break;
}
default:
{
GPIO_TogglePin(CW_GPIOA, Led1_Pin);
break;
}
}
}KEY
底板按键
- 硬件连接
- 程序编写
key1.h
#ifndef __KEY_1_H
#define __KEY_1_H
#include "main.h"
// 管脚 K1--PB13 K2--PB14 K3--PB15
#define Key1_Pin GPIO_PIN_13
#define Key2_Pin GPIO_PIN_14
#define Key3_Pin GPIO_PIN_15
// 读取按键电平
#define READ_KEY1 GPIO_ReadPin(CW_GPIOB,GPIO_PIN_13)
#define READ_KEY2 GPIO_ReadPin(CW_GPIOB,GPIO_PIN_14)
#define READ_KEY3 GPIO_ReadPin(CW_GPIOB,GPIO_PIN_15)
typedef enum
{
KEY_NULL = (uint8_t)0x00, // 无按键按下键值
KEY1_DOWN = (uint8_t)0x01, // 按键1按下键值
KEY2_DOWN = (uint8_t)0x02, // 按键2按下键值
KEY3_DOWN = (uint8_t)0x03, // 按键3按下键值
}Key_1_Status_t;
typedef struct
{
uint16_t volatile vusKey_1_Timer_Count; // 长按计数
uint8_t volatile vucKey_1_Flag_Arr[6]; // 按键标志位(短长按)
void (*Key_1_Init)(void); // 按键初始化
void (*Key_1_Scan)(void); // 按键三行消抖---按键扫描
void (*Key_1_Handler)(void); // 按键处理
}Key_1_t;
extern Key_1_t Key_1;
#endifkey1.c
/***************************************************************************
* File: key_1.c
* Author: Luckys.
* Date: 2023/06/11
* description: 底板独立按键(低电平有效)
****************************************************************************/
#include "main.h"
/*====================================variable definition declaration area BEGIN===================================*/
// 按键键值、抬起一瞬间、按下一瞬间
static uint8_t ucKey_1_Value,ucKey_1_Up,ucKey_1_Down;
/*====================================variable definition declaration area END===================================*/
/*====================================static function declaration area BEGIN====================================*/
static void Key_1_Init(void); // 按键初始化
static uint8_t Key_1_Return_Value(void); // 返回键值
static void Key_1_Scan(void); // 按键三行消抖---按键扫描
static void Key_1_Handler(void); // 按键处理
/*====================================static function declaration area END====================================*/
Key_1_t Key_1 =
{
0,
{FALSE},
Key_1_Init,
Key_1_Scan,
Key_1_Handler,
};
/*
* @function: Key_1_Init
* @param: None
* @retval: None
* @brief: 按键初始化
*/
static void Key_1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能时钟
__RCC_GPIOB_CLK_ENABLE();
GPIO_InitStructure.IT = GPIO_IT_NONE; // 管脚中断模式--无
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP; // 模式--上拉输入
GPIO_InitStructure.Pins = Key1_Pin | Key2_Pin | Key3_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
}
/*
* @function: Key_1_Return_Value
* @param: None
* @retval: None
* @brief: 返回键值
*/
static uint8_t Key_1_Return_Value(void)
{
if ((!READ_KEY1) || (!READ_KEY2) || (!READ_KEY3))
{
if (!READ_KEY1)
{
return KEY1_DOWN;
}
else if (!READ_KEY2)
{
return KEY2_DOWN;
}
else if (!READ_KEY3)
{
return KEY3_DOWN;
}
}
return KEY_NULL;
}
/*
* @function: Key_1_Scan
* @param: None
* @retval: None
* @brief: 按键三行消抖---按键扫描
*/
static void Key_1_Scan(void)
{
static uint8_t uckey_1_old;
ucKey_1_Value = Key_1_Return_Value(); // 读取按键的键值
ucKey_1_Up = ~ucKey_1_Value & (uckey_1_old ^ ucKey_1_Value); // 按键的上升沿检测 只在按键抬起的瞬间有效 其他时刻都为零无效
ucKey_1_Down = ucKey_1_Value & (uckey_1_old ^ ucKey_1_Value); // 按键的下降沿检测 只在按键按下的瞬间有效 其他时刻都为零无效
uckey_1_old = ucKey_1_Value; // 记录上一次按键按下后的键值
if (ucKey_1_Down) // 当有按键按下时
{
Key_1.vusKey_1_Timer_Count = 0; // 将计时器清零 从零开始计时 此处使用了基础定时器用于计时
}
if (Key_1.vusKey_1_Timer_Count < 10) // 如果计时时间小于1s 短按
{
switch (ucKey_1_Up) // 判断按键是否抬起 选择键值执行短按的相应程序
{
case KEY1_DOWN:Key_1.vucKey_1_Flag_Arr[0] = TRUE;break;
case KEY2_DOWN:Key_1.vucKey_1_Flag_Arr[1] = TRUE;break;
case KEY3_DOWN:Key_1.vucKey_1_Flag_Arr[2] = TRUE;break;
default:break;
}
}
else // 长按 计时时间超过1s
{
switch (ucKey_1_Value) // 判断按键是否抬起 选择键值执行短按的相应程序
{
case KEY1_DOWN:Key_1.vucKey_1_Flag_Arr[3] = TRUE;break;
case KEY2_DOWN:Key_1.vucKey_1_Flag_Arr[4] = TRUE;break;
case KEY3_DOWN:Key_1.vucKey_1_Flag_Arr[5] = TRUE;break;
default:break;
}
}
}
/*
* @function: Key_1_Handler
* @param: None
* @retval: None
* @brief: 按键处理
*/
static void Key_1_Handler(void)
{
if (Key_1.vucKey_1_Flag_Arr[0]) // K1短按
{
if (Buzzer.Buzzer_Status == Buzzer_Status_OFF)
{
Buzzer.Buzzer_ON();
}
else
{
Buzzer.Buzzer_OFF();
}
Key_1.vucKey_1_Flag_Arr[0] = FALSE;
}
else if (Key_1.vucKey_1_Flag_Arr[1]) // K2短按
{
// 切换页面
Menu.Now_Page_Status = (Menu.Now_Page_Status % PAGE_MAX) + 1;
OLED096.OLED096_Clear();
Key_1.vucKey_1_Flag_Arr[1] = FALSE;
}
else if (Key_1.vucKey_1_Flag_Arr[2]) //K3短按
{
Key_1.vucKey_1_Flag_Arr[2] = FALSE;
}
else if (Key_1.vucKey_1_Flag_Arr[3]) // K1长按
{
Gtim.Gtim1_CH_Set_Pulse[0] += 20; // 占空比加5%
Gtim.Gtim1_CH_Set_Pulse[1] += 20; // 占空比加5%
if (Gtim.Gtim1_CH_Set_Pulse[0] > 380)
{
Gtim.Gtim1_CH_Set_Pulse[0] = 20; // 占空比恢复到5%
}
if (Gtim.Gtim1_CH_Set_Pulse[1] > 380)
{
Gtim.Gtim1_CH_Set_Pulse[1] = 40; // 占空比恢复到10%
}
// 更新占空比
CW_GTIM1->CCR1 = Gtim.Gtim1_CH_Set_Pulse[0];
CW_GTIM1->CCR2 = Gtim.Gtim1_CH_Set_Pulse[1];
Key_1.vucKey_1_Flag_Arr[3] = FALSE;
}
else if (Key_1.vucKey_1_Flag_Arr[4]) // K2长按
{
Key_1.vucKey_1_Flag_Arr[4] = FALSE;
}
}
最小系统板按键+外部中断
- 硬件连接
- 程序编写
key_2.h
#ifndef __KEY_2_H
#define __KEY_2_H
#include "main.h"
// 管脚 SW1--PB2
#define SW1_Pin GPIO_PIN_2
// 读取按键电平
#define READ_SW1 GPIO_ReadPin(CW_GPIOB,GPIO_PIN_2)
typedef struct
{
void (*Key_2_Init)(void); // 按键初始化
}Key_2_t;
extern Key_2_t Key_2;
#endifkey_2.c
/***************************************************************************
* File: key_2.c
* Author: Luckys.
* Date: 2023/06/13
* description: 最小系统板的按键---外部中断方式(按键高电平有效)
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void Key_2_Init(void); // 按键初始化
/*====================================static function declaration area END====================================*/
Key_2_t Key_2 =
{
Key_2_Init,
};
/*
* @function: Key_2_Init
* @param: None
* @retval: None
* @brief: 按键初始化
*/
static void Key_2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能时钟
__RCC_GPIOB_CLK_ENABLE();
GPIO_InitStructure.IT = GPIO_IT_RISING; // 管脚中断模式--上升沿触发
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLDOWN; // 模式--下拉输入
GPIO_InitStructure.Pins = SW1_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
GPIOB_INTFLAG_CLR(bv2); // 清除PB2中断标志
NVIC_EnableIRQ(GPIOB_IRQn); // 使能NVIC
}
编写中断
蜂鸣器
- 硬件连接
- 程序编写
buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H
#include "main.h"
// 管家定义 buzzer--PB3
#define Buzzer_Pin GPIO_PIN_3
typedef enum
{
Buzzer_Status_ON = (uint8_t)0x01, // 蜂鸣器打开中态
Buzzer_Status_OFF = (uint8_t)0x00, // 蜂鸣器关闭态
}Buzzer_Status_t;
typedef struct
{
Buzzer_Status_t Buzzer_Status; // 蜂鸣器状态
void (*Buzzer_Init)(void); // 蜂鸣器初始化
void (*Buzzer_ON)(void); // 蜂鸣器打开
void (*Buzzer_OFF)(void); // 蜂鸣器关闭
}Buzzer_t;
extern Buzzer_t Buzzer;
#endifbuzzer.c
/***************************************************************************
* File: buzzer.c
* Author: Luckys.
* Date: 2023/06/12
* description: 蜂鸣器
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void Buzzer_Init(void); // 蜂鸣器初始化
static void Buzzer_ON(void); // 蜂鸣器打开
static void Buzzer_OFF(void); // 蜂鸣器关闭
/*====================================static function declaration area END====================================*/
Buzzer_t Buzzer =
{
Buzzer_Status_OFF,
Buzzer_Init,
Buzzer_ON,
Buzzer_OFF,
};
/*
* @function: Buzzer_Init
* @param: None
* @retval: None
* @brief: 蜂鸣器初始化
*/
static void Buzzer_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能时钟
__RCC_GPIOB_CLK_ENABLE();
GPIO_InitStructure.IT = GPIO_IT_NONE; // 管脚中断模式--无
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 模式--推挽输出
GPIO_InitStructure.Pins = Buzzer_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
GPIO_WritePin(CW_GPIOB,Buzzer_Pin,GPIO_Pin_RESET); // 默认蜂鸣器关闭
}
/*
* @function: Buzzer_ON
* @param: None
* @retval: None
* @brief: 蜂鸣器打开
*/
static void Buzzer_ON(void)
{
GPIO_WritePin(CW_GPIOB,Buzzer_Pin,GPIO_Pin_SET);
Buzzer.Buzzer_Status = Buzzer_Status_ON;
}
/*
* @function: Buzzer_OFF
* @param: None
* @retval: None
* @brief: 蜂鸣器关闭
*/
static void Buzzer_OFF(void)
{
GPIO_WritePin(CW_GPIOB,Buzzer_Pin,GPIO_Pin_RESET);
Buzzer.Buzzer_Status = Buzzer_Status_OFF;
}
串口
有三个串口分别是 USART1,USART2,USART3
这里我们使用连接link下载器的那个串口来进行实验—
USART1_TX --- PB8,USART1_RX --- PB9,因为PA13,PA14被SW占用了,试了那个没效果接线方式:
单片机PB8 ---- Wch-link RXD
单片机PB9 ---- Wch-link TXD
然后这个单片机它没有空闲中断,所以只能普通中断进行接收
- 程序编写
普通发送+中断接收+模拟Modbus协议
串口发送的话需要注意发送一个字节需要判断等待发送完成再发,不然可能每次只发送成功2个字节后面的就丢了
试了添加了超时机制,发送后等待一段时间等待发送完成标志位置1(UART_TX_TimerOut是枚举值是100)
cppuint32_t TimerOut = GetTick() + UART_TX_TimerOut; // 获取当前计数值+串口超时时间(ms) while ((USART_GetFlagStatus(UART_DEBUG, USART_FLAG_TXE) == RESET) && (TimerOut--)) { if (0 == TimerOut) { System.Error_Handler(); // 进入错误处理 return; } }
usart1.h
#ifndef __USART1_H
#define __USART1_H
#include "main.h"
// 串口发送长度,接收长度
#define USART1_Send_LENGTH 20
#define USART1_Rec_LENGTH 100
// 串口1引脚 PB9(RX) PB8(TX)
#define Usart1_Tx_Pin GPIO_PIN_8
#define Usart1_Rx_Pin GPIO_PIN_9
typedef struct
{
uint8_t ucUsart1_Rx_Cnt; // 接收长度计数
uint8_t* puc_Usart1_Send_Buffer; // 发送缓存指针
uint8_t* puc_Usart1_Rec_Buffer; // 接收缓存指针
void (*USART1_Init)(void); // 串口1初始化
void (*USART1_Send_Array)(uint8_t*, uint16_t); // 发送数组
void (*USART1_Send_String)(uint8_t*); // 发送字符串
}USART1_t;
extern USART1_t USART1;
#endifusart1.c
/***************************************************************************
* File: usart1.c
* Author: Luckys.
* Date: 2023/06/12
* description: USART1
-----------------------------------
USART1:TX--PB8 RX--PB9
接线:
单片机PB8 ---- Wch-link RXD
单片机PB9 ---- Wch-link TXD
-----------------------------------
****************************************************************************/
#include "main.h"
/*====================================variable definition declaration area BEGIN===================================*/
static uint8_t Usart1_Send_Bufferp[USART1_Send_LENGTH] = {0x00}; // 发送数组
static uint8_t Usaer1_Rec_Buffer[USART1_Rec_LENGTH] = {0x00}; // 接收数据
/*====================================variable definition declaration area END===================================*/
/*====================================static function declaration area BEGIN====================================*/
static void USART1_Init(void); // 串口1初始化
static void USART1_Send_Array(uint8_t*, uint16_t); // 发送数组
static void USART1_Send_String(uint8_t*); // 发送字符串
/*====================================static function declaration area END====================================*/
USART1_t USART1 =
{
0,
Usart1_Send_Bufferp,
Usaer1_Rec_Buffer,
USART1_Init,
USART1_Send_Array,
USART1_Send_String,
};
/*
* @function: USART1_Init
* @param: None
* @retval: None
* @brief: 串口1初始化
*/
static void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
USART_InitTypeDef USART_InitStructure; //定义结构体
__RCC_GPIOB_CLK_ENABLE(); // 使能引脚时钟
__RCC_UART1_CLK_ENABLE(); // 使能串口时钟
// 打开复用
PB08_AFx_UART1TXD();
PB09_AFx_UART1RXD();
/*端口初始化*/
GPIO_InitStructure.IT = GPIO_IT_NONE; // 管脚中断模式--无
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 模式--推挽输出
GPIO_InitStructure.Pins = Usart1_Tx_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.Mode = GPIO_MODE_INPUT; // 模式--浮空输入
GPIO_InitStructure.Pins = Usart1_Rx_Pin;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
/*串口初始化*/
CW_UART1->CR1_f.SYNC = 0; // 0-异步全双工通信模式; 1-同步半双工通信模式
CW_UART1->ICR = 0x00; // 清除所有串口中断标志
USART_InitStructure.USART_BaudRate = 9600; // 波特率
USART_InitStructure.USART_Over = USART_Over_16; // 采样方式---16倍采样
USART_InitStructure.USART_StopBits = USART_StopBits_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; //收发模式
// 下面3条是CW特有的
USART_InitStructure.USART_UclkFreq = 64000000; // 传输时钟UCLK频率 Hz
USART_InitStructure.USART_StartBit = USART_StartBit_FE; // 起始位判定方式---下降沿(低功耗则选择低电平)
USART_InitStructure.USART_Source = USART_Source_PCLK; // 传输时钟源UCLK---PCLK(看时钟树)
USART_Init(CW_UART1,&USART_InitStructure); // 串口初始化
/*NVIC初始化(不需要定义结构体)*/
NVIC_SetPriority(UART1_IRQn,0); // 优先级,无优先级分组
NVIC_EnableIRQ(UART1_IRQn); // 使能中断---中断通道号
USART_ITConfig(CW_UART1,USART_IT_RC,ENABLE); // 接收完成中断使能
}
/*
* @function: USART1_Send_Array
* @param: p_Arr -> 待发送数组 LEN -> 数组长度(使用sizeof计算)
* @retval: None
* @brief: 发送数组
*/
static void USART1_Send_Array(uint8_t* p_Arr, uint16_t LEN)
{
uint16_t i;
for (i = 0; i < LEN; i++)
{
USART_SendData_8bit(CW_UART1,*(p_Arr + i));
// 等待发送完成,1:完成,0:还没完成
while (USART_GetFlagStatus(UART_DEBUG, USART_FLAG_TXE) == RESET);
}
while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXBUSY) == SET); // 等待串口空闲
}
/*
* @function: USART1_Send_String
* @param: p_Str -> 待发送字符串
* @retval: None
* @brief: 发送字符串
*/
static void USART1_Send_String(uint8_t* p_Str)
{
while (*p_Str)
{
USART_SendData_8bit(CW_UART1,*p_Str);
// 等待发送完成,1:完成,0:还没完成
while (USART_GetFlagStatus(UART_DEBUG, USART_FLAG_TXE) == RESET);
p_Str++;
}
while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXBUSY) == SET); // 等待串口空闲
}
编写中断函数
modbus.h
#ifndef __MODBUS_H
#define __MODBUS_H
#include "main.h"
// 定义---读写寄存器功能号,命令长度
#define FunctionCode_Read_Register (uint8_t)0x03
#define FunctionCode_Write_Register (uint8_t)0x06
#define Modbus_Order_LENGTH (uint8_t)8
typedef struct
{
uint16_t Addr; // 地址
void (*Protocol_Analysis)(USART1_t *); // 协议分析
} Modbus_t;
extern Modbus_t Modbus;
#endifmodbus.c
/***************************************************************************
* File: modbus.c
* Author: Luckys.
* Date: 2023/06/12
* description: ModBus协议
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void Protocol_Analysis(USART1_t*); //协议分析
static void Modbus_Read_Register(USART1_t*); //读寄存器
static void Modbus_Wrtie_Register(USART1_t*); //写寄存器
/*====================================static function declaration area END====================================*/
Modbus_t Modbus =
{
1,
Protocol_Analysis
};
/*
* @function: Protocol_Analysis
* @param: UART -> 串口1结构体指针
* @retval: None
* @brief: 描述
*/
static void Protocol_Analysis(USART1_t *UART)
{
USART1_t *const COM = UART;
uint8_t i = 0, Index = 0;
// 过滤干扰数据,首字节为modbus地址,共8字节
for (i = 0; i < USART1_Rec_LENGTH; i++)
{
// 检测键值起始数据Modbus.Addr
if (Index == 0)
{
if (*(COM->puc_Usart1_Rec_Buffer + i) != Modbus.Addr)
// 跳过下面的代码i++进行下一次循环
continue;
}
*(COM->puc_Usart1_Rec_Buffer + Index) = *(COM->puc_Usart1_Rec_Buffer + i);
// 已读取8个字节
if (Index == Modbus_Order_LENGTH)
{
break;
}
Index++;
}
// 计算CRC-16
CRC_16.CRC_Value = CRC_16.CRC_Check(COM->puc_Usart1_Rec_Buffer, 6); // 计算CRC值
CRC_16.CRC_H = (uint8_t)(CRC_16.CRC_Value >> 8);
CRC_16.CRC_L = (uint8_t)CRC_16.CRC_Value;
// 校验CRC-16(为了兼容不同市面上的协议,高字节在前或者低字节在前)
if (((*(COM->puc_Usart1_Rec_Buffer + 6) == CRC_16.CRC_L) && (*(COM->puc_Usart1_Rec_Buffer + 7) == CRC_16.CRC_H)) ||
((*(COM->puc_Usart1_Rec_Buffer + 6) == CRC_16.CRC_H) && (*(COM->puc_Usart1_Rec_Buffer + 7) == CRC_16.CRC_L)))
{
// 校验地址
if ((*(COM->puc_Usart1_Rec_Buffer + 0)) == Modbus.Addr)
{
// 处理数据
if ((*(COM->puc_Usart1_Rec_Buffer + 1)) == FunctionCode_Read_Register)
{
Modbus_Read_Register(COM);
}
else if ((*(COM->puc_Usart1_Rec_Buffer + 1)) == FunctionCode_Write_Register)
{
Modbus_Wrtie_Register(COM);
}
}
}
// 清缓存
Public.Memory_Clear(COM->puc_Usart1_Rec_Buffer, USART1_Rec_LENGTH);
USART1.ucUsart1_Rx_Cnt = 0;
}
/*
* @function: Modbus_Read_Register
* @param: UART -> 串口1结构体指针
* @retval: None
* @brief: 读寄存器
*/
static void Modbus_Read_Register(USART1_t *UART)
{
USART1_t *const COM = UART;
//校验地址
if((*(COM->puc_Usart1_Rec_Buffer + 2) == 0x9C) && (*(COM->puc_Usart1_Rec_Buffer + 3) == 0x41))
{
////回应数据
//地址码
*(COM->puc_Usart1_Send_Buffer + 0) = Modbus.Addr;
//功能码
*(COM->puc_Usart1_Send_Buffer + 1) = FunctionCode_Read_Register;
//数据长度
*(COM->puc_Usart1_Send_Buffer + 2) = 2;
//蜂鸣器状态
*(COM->puc_Usart1_Send_Buffer + 3) = 0;
*(COM->puc_Usart1_Send_Buffer + 4) = Buzzer.Buzzer_Status;
//插入CRC
CRC_16.CRC_Value = CRC_16.CRC_Check(COM->puc_Usart1_Send_Buffer, 5); //计算CRC值,因为CRC前有5个字节
CRC_16.CRC_H = (uint8_t)(CRC_16.CRC_Value >> 8);
CRC_16.CRC_L = (uint8_t)CRC_16.CRC_Value;
// 低位在前高位在后
*(COM->puc_Usart1_Send_Buffer + 5) = CRC_16.CRC_L;
*(COM->puc_Usart1_Send_Buffer + 6) = CRC_16.CRC_H;
//发送数据
USART1.USART1_Send_Array(COM->puc_Usart1_Send_Buffer, 7);
printf("\r\n"); // VOFA+上位机需要的!
}
}
/*
* @function: Modbus_Wrtie_Register
* @param: UART -> 串口1结构体指针
* @retval: None
* @brief: 描述
*/
static void Modbus_Wrtie_Register(USART1_t *UART)
{
USART1_t *const COM = UART;
uint8_t i;
////回应数据
// 准备数据
for (i = 0; i < 8; i++)
{
*(COM->puc_Usart1_Send_Buffer + i) = *(COM->puc_Usart1_Rec_Buffer + i);
}
// 发送数据
USART1.USART1_Send_Array(COM->puc_Usart1_Send_Buffer, 8);
printf("\r\n"); // VOFA+上位机需要的!
// 校验地址 -> 蜂鸣器
if ((*(COM->puc_Usart1_Rec_Buffer + 2) == 0x9C) && (*(COM->puc_Usart1_Rec_Buffer + 3) == 0x44)) // 40004
{
// 控制蜂鸣器
if (*(COM->puc_Usart1_Rec_Buffer + 5) == 0x01)
{
Buzzer.Buzzer_ON();
}
else
{
Buzzer.Buzzer_OFF();
}
}
}
CRC_16.h和CRC_16.c 跟之前的modbus一样复制即可这里就不展示了
发跟收都要加
0x0D 0x0A(\r\n)
- 上位机通过发送
01 03 9C 41 00 01 4E FA去向单片机读取蜂鸣器状态,单片机回复01 03 02 00 00 44 B8或者01 03 02 00 01 84 79表示响和不响- 上位机发送
01 06 9C 44 00 01 4F 26控制单片机蜂鸣器响,发送01 06 9C 44 00 00 8F E7控制单片机蜂鸣器不响
定时器
数据手册介绍
只是使用定时器计数功能,不需要占用引脚资源,而当使用定时器通道时,则需要使用相应的引脚来输出或捕获信号(所以使用通道需要看手册的复用功能考虑用哪个)
重装载值最大是65535,分频系数它有给选项不能自定义(取值范围2的n次幂)
基本定时器
定时器模式(常用)
btim.h
#ifndef __BTIM_H
#define __BTIM_H
#include "main.h"
typedef struct
{
void (*BTIM1_Init)(uint16_t, uint16_t); // BTIM1初始化
}Btim_t;
extern Btim_t Btim;
#endifbtim.c
/***************************************************************************
* File: btim.c
* Author: Luckys.
* Date: 2023/06/13
* description: 基本定时器
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void BTIM1_Init(uint16_t, uint16_t); // BTIM1初始化
/*====================================static function declaration area END====================================*/
Btim_t Btim =
{
BTIM1_Init,
};
/*
* @function: BTIM1_Init
* @param: arr -> 重装载值 psc -> 时钟分频系数(例如BTIM_PRS_DIV1)
* @retval: None
* @brief: BTIM1初始化
*/
static void BTIM1_Init(uint16_t arr, uint16_t psc)
{
BTIM_TimeBaseInitTypeDef BTM_TimerBaseInitStructure;
__RCC_BTIM_CLK_ENABLE(); // 打开定时器时钟
__disable_irq(); // 关闭中断
NVIC_EnableIRQ(BTIM1_IRQn); // 使能BTIM1中断
__enable_irq(); // 打开中断
BTM_TimerBaseInitStructure.BTIM_Mode = BTIM_Mode_TIMER; // 模式---定时器模式
BTM_TimerBaseInitStructure.BTIM_OPMode = BTIM_OPMode_Repetitive; // 连续模式
BTM_TimerBaseInitStructure.BTIM_Period = arr; // 重装载值
BTM_TimerBaseInitStructure.BTIM_Prescaler = psc; // 时钟预分频系数
BTIM_TimeBaseInit(CW_BTIM1, &BTM_TimerBaseInitStructure); //配置BTIM1定时器
BTIM_ITConfig(CW_BTIM1, BTIM_IT_OV, ENABLE); //使能BTIM1中断
BTIM_Cmd(CW_BTIM1, ENABLE); //使能定时器
}通用定时器
- 手册
PWM普通输出
电平极性在头文件有定义
cpp#define GTIM_OC_OUTPUT_PWM_HIGH (14UL) #define GTIM_OC_OUTPUT_PWM_LOW (15UL)需要注意的是PWM_HIGH输出的有效电平是低电平,PWM_LOW输出的有效电平是高电平,用的时候注意即可
gtim.h
#ifndef __GHTIM_H
#define __GHTIM_H
#include "main.h"
typedef struct
{
uint16_t Gtim1_CH_Fre; // GTIM频率
float Gtim1_CH_Duty[2]; // GTIM1 通道占空比
uint16_t Gtim1_CH_Set_Pulse[2]; // 设置占空比(2个通道的)
void (*Gtim1_PWM_Output)(uint16_t, uint16_t); // 通用定时器1PWM输出初始化
void (*Gtim1_Calculate)(void); // 计算频率占空比
}Gtim_t;
extern Gtim_t Gtim;
#endif
gtim.c
/***************************************************************************
* File: gtim.c
* Author: Luckys.
* Date: 2023/06/13
* description: 通用定时器
-----------------------------------
用到:
PWM普通输出:
GTIM1: PB4 --- CH1 PB5 --- CH2
-----------------------------------
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void Gtim1_PWM_Output(uint16_t arr, uint16_t psc); // 通用定时器1PWM输出初始化
static void Gtim1_Calculate(void); // 计算频率占空比
/*====================================static function declaration area END====================================*/
Gtim_t Gtim =
{
0,
{0.0, 0.0},
{200, 200},
Gtim1_PWM_Output,
Gtim1_Calculate,
};
/*
* @function: Gtim1_PWM_Output
* @param: arr -> 重装载值 psc -> 时钟分频系数(例如 GTIM_PRESCALER_DIV64)
* @retval: None
* @brief: 通用定时器1PWM输出初始化
*/
static void Gtim1_PWM_Output(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
GTIM_InitTypeDef GTIM_InitStructure;
// 使能时钟
__RCC_GTIM1_CLK_ENABLE();
__RCC_GPIOB_CLK_ENABLE();
PB04_AFx_GTIM1CH1(); // PB4复用为GTIM1-CH1
PB05_AFx_GTIM1CH2(); // PB5复用为GTIM1-CH2
GPIO_InitStructure.IT = GPIO_IT_NONE; // 管脚中断模式--无
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 模式--推挽输出
GPIO_InitStructure.Pins = GPIO_PIN_4 | GPIO_PIN_5;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
/*定时器配置*/
GTIM_InitStructure.Mode = GTIM_MODE_TIME; // 定时器模式 --- 定时器模式
GTIM_InitStructure.OneShotMode = GTIM_COUNT_CONTINUE; // 单次/连续计数模式 --- 连续
GTIM_InitStructure.Prescaler = psc; // 预分频系数 --- psc
GTIM_InitStructure.ReloadValue = arr; // 重载值 --- arr
GTIM_InitStructure.ToggleOutState = DISABLE; // 翻转输出使能选择 --- 不使能
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStructure); // 初始化
/*通道1*/
GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL1, GTIM_OC_OUTPUT_PWM_LOW); // 比较输出功能初始化 --- 定时器 通道 有效电平极性-高电平
/*通道2*/
GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL2, GTIM_OC_OUTPUT_PWM_HIGH); // 比较输出功能初始化 --- 定时器 通道 有效电平极性-高电平
/*设置初始占空比*/
CW_GTIM1->CCR1 = Gtim.Gtim1_CH_Set_Pulse[0]; // 设置通道1初始占空比 --- 初始化默认50%占空比
CW_GTIM1->CCR2 = Gtim.Gtim1_CH_Set_Pulse[1]; // 设置通道2初始占空比 --- 初始化默认50%占空比
GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE); // 中断打开
GTIM_Cmd(CW_GTIM1, ENABLE); // 使能定时器
// 使能中断
__disable_irq();
NVIC_EnableIRQ(GTIM1_IRQn);
__enable_irq();
}输入捕获
通道1的输入捕获中断获取计数值VALUE1,通道2的输入捕获中断获取计数值VALUE2,通道1的第2次输入捕获中断获取计数值VALUE3。则信号脉宽=VALUE2-VALUE1,信号周期=VALUE3-VALUE1。注意如果待测量信号的脉宽和周期较长,在计算时需要考虑定时器的溢出问题
这部分有BUG,不能检测到待解决 —2023/6/18
callback.c
/*
* @function: GTIM2_IRQHandler
* @param: None
* @retval: None
* @brief: GTIM2中断服务函数
*/
void GTIM2_IRQHandler(void)
{
static uint8_t Status = 0; // 标志位,用于表示当前处于PWM信号的哪一阶段
static uint32_t cnt = 0; // 计数
if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_OV)) // 判断是否为GTIM1计数器溢出中断
{
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_OV); // 清除计数器溢出中断标志位
if (Status == 1) // 如果当前处于PWM信号的第二阶段
{
cnt++;
}
}
if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC1)) // 判断是否为GTIM1捕获比较匹配中断
{
if (Status == 0) // 如果当前处于PWM信号的第一阶段
{
Gtim.Gtim2_IC_Fre = CW_GTIM2->CCR1; // 获取捕获比较器1的值,即为PWM信号的周期
Status = 1; // 切换至PWM信号的第二阶段
}
else if (Status == 1) // 如果当前处于PWM信号的第二阶段
{
Gtim.Gtim2_IC_Fre = CW_GTIM2->CCR1 + cnt * 65536 - Gtim.Gtim2_IC_Fre; // 计算PWM信号的周期
Status = 0; // 切换至PWM信号的第一阶段
cnt = 0; // 计数器清零
}
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC1); // 清除捕获比较器1匹配中断标志位
}
if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC2)) // 判断是否为GTIM1捕获比较匹配中断
{
if (Status == 1) // 如果当前处于PWM信号的第二阶段
{
Gtim.Gtim2_IC_Duty = CW_GTIM2->CCR2 + cnt * 65536 - Gtim.Gtim2_IC_Fre; // 计算PWM信号的占空比
Gtim.Gtim2_IC_Flag = TRUE;
}
GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC2); // 清除捕获比较器2匹配中断标志位
}
}gtim.c
/*
* @function: Gtim2_PWM_IC_Init
* @param: None
* @retval: None
* @brief: GTIM2输入捕获初始化
*/
static void Gtim2_PWM_IC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GTIM_InitTypeDef GTIM_InitStrucure;
GTIM_ICInitTypeDef GTIM_ICInitStructure;
__RCC_GTIM2_CLK_ENABLE(); //
__RCC_GPIOA_CLK_ENABLE();
/*GPIO*/
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pins = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
PA00_AFx_GTIM2CH1(); // AF
PA01_AFx_GTIM2CH2();
__disable_irq();
NVIC_EnableIRQ(GTIM2_IRQn);
__enable_irq();
GTIM_InitStrucure.Mode = GTIM_MODE_TIME;
GTIM_InitStrucure.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStrucure.Prescaler = GTIM_PRESCALER_DIV1;
GTIM_InitStrucure.ReloadValue = 0xFFFF; // ARR
GTIM_InitStrucure.ToggleOutState = DISABLE;
GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStrucure);
GTIM_ICInitStructure.CHx = GTIM_CHANNEL1; // CH1
GTIM_ICInitStructure.ICFilter = GTIM_CHx_FILTER_NONE; // 过滤器 -- 无
GTIM_ICInitStructure.ICInvert = GTIM_CHx_INVERT_ON; // 翻转 --- 无
GTIM_ICInitStructure.ICPolarity = GTIM_ICPolarity_Rising; // 上升沿
GTIM_ICInit(CW_GTIM2, >IM_ICInitStructure);
GTIM_ICInitStructure.CHx = GTIM_CHANNEL2; // CH2
GTIM_ICInitStructure.ICPolarity = GTIM_ICPolarity_Falling; // 下降沿
GTIM_ICInit(CW_GTIM2, >IM_ICInitStructure);
GTIM_ITConfig(CW_GTIM2, GTIM_IT_CC1 | GTIM_IT_CC2 | GTIM_IT_OV, ENABLE);
GTIM_Cmd(CW_GTIM2, ENABLE);
}PWM互补输出
各占一半频率,比如设置2500Hz,则两个通道各占一半1.25Hz
gtim.c
/*
* @function: Gtim3_PWM_Toggle_Init
* @param: arr -> 重装载值 psc -> 预分频(比如 GTIM_PRESCALER_DIV1024)
* @retval: None
* @brief: GTIM3互补输出初始化
*/
static void Gtim3_PWM_Toggle_Init(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
GTIM_InitTypeDef GTIM_InitStrucure;
__RCC_GTIM3_CLK_ENABLE(); //
__RCC_GPIOC_CLK_ENABLE();
PC14_AFx_GTIM3TOGN(); // AF
PC15_AFx_GTIM3TOGP();
/*GPIO*/
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pins = GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
GTIM_InitStrucure.Mode = GTIM_MODE_TIME;
GTIM_InitStrucure.OneShotMode = GTIM_COUNT_CONTINUE;
GTIM_InitStrucure.Prescaler = psc;
GTIM_InitStrucure.ReloadValue = arr; // ARR
GTIM_InitStrucure.ToggleOutState = ENABLE;
GTIM_TimeBaseInit(CW_GTIM3, >IM_InitStrucure);
GTIM_Cmd(CW_GTIM3, ENABLE);
}高级定时器
输入捕获
输入捕获也是有问题
atim.c
/*
* @function: Atim_PWM_Input_Init
* @param: None
* @retval: None
* @brief: 输入捕获初始化
*/
static void Atim_PWM_Input_Init(void)
{
ATIM_InitTypeDef ATIM_InitStructure;
ATIM_ICInitTypeDef ATIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
__RCC_GPIOB_CLK_ENABLE();
__RCC_ATIM_CLK_ENABLE();
/*gpio init*/
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pins = GPIO_PIN_2;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
PB02_AFx_ATIMCH1A(); // PB2 --- CH1A
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
/*open nvic*/
__disable_irq();
NVIC_EnableIRQ(ATIM_IRQn);
__enable_irq();
ATIM_InitStructure.BufferState = DISABLE;
ATIM_InitStructure.ClockSelect = ATIM_CLOCK_PCLK;
ATIM_InitStructure.CounterAlignedMode = ATIM_COUNT_MODE_EDGE_ALIGN;
ATIM_InitStructure.CounterDirection = ATIM_COUNTING_UP;
ATIM_InitStructure.CounterOPMode = ATIM_OP_MODE_REPETITIVE;
ATIM_InitStructure.OverFlowMask = DISABLE;
ATIM_InitStructure.Prescaler = ATIM_Prescaler_DIV64;
ATIM_InitStructure.ReloadValue = 0xFFFF;
ATIM_InitStructure.RepetitionCounter = 0;
ATIM_InitStructure.UnderFlowMask = DISABLE;
ATIM_Init(&ATIM_InitStructure);
ATIM_ICInitStructure.ICFilter = ATIM_ICFILTER_NONE; // 输入滤波配置
ATIM_ICInitStructure.ICPolarity = ATIM_ICPOLARITY_BOTHEDGE; // 输入捕获极性:上升、下降、双沿
ATIM_IC1AInit(&ATIM_ICInitStructure); // 输入捕获通道1A设置
ATIM_ITConfig(ATIM_CR_IT_OVE, ENABLE); // ATIM中断设置
ATIM_CH1Config(ATIM_CHxA_CIE, ENABLE); // 设置通道1的功能
ATIM_Cmd(ENABLE);
}callback.c
uint32_t PWMPeriod = 0;
uint32_t PWMWidth = 0;
uint8_t ProcessState = 0;
/*
* @function: ATIM_IRQHandler
* @param: None
* @retval: None
* @brief: 高级定时器中断服务函数
*/
void ATIM_IRQHandler(void)
{
static uint8_t stage = 0;
static uint32_t cnt = 0;
if (ATIM_GetITStatus(ATIM_IT_OVF))
{
ATIM_ClearITPendingBit(ATIM_IT_OVF);
if (stage)
{
cnt++;
}
}
if (ATIM_GetITStatus(ATIM_IT_C1AF))
{
ATIM_ClearITPendingBit(ATIM_IT_C1AF);
if (stage == 0)
{
PWMPeriod = ATIM_GetCapture1A();
cnt = 0;
stage++;
}
else if (stage == 1)
{
PWMWidth = ATIM_GetCapture1A() + cnt * 0x10000UL - PWMPeriod;
stage++;
}
else if (stage == 2)
{
PWMPeriod = ATIM_GetCapture1A() + cnt * 0x10000UL - PWMPeriod;
stage = 0;
ProcessState = 1;
}
}
}输出比较
atim.c
/*
* @function: Atim_PWM_Output_OC_Init
* @param: None
* @retval: None
* @brief: 输出比较初始化
*/
static void Atim_PWM_Output_OC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ATIM_InitTypeDef ATIM_InitStructure;
ATIM_OCInitTypeDef ATIM_OCInitStructure;
__RCC_ATIM_CLK_ENABLE();
__RCC_GPIOB_CLK_ENABLE();
/*gpio init*/
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pins = GPIO_PIN_2 | GPIO_PIN_13;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
PB02_AFx_ATIMCH1A();
PB13_AFx_ATIMCH1B();
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
__disable_irq();
NVIC_EnableIRQ(ATIM_IRQn);
__enable_irq();
ATIM_InitStructure.BufferState = DISABLE;
ATIM_InitStructure.ClockSelect = ATIM_CLOCK_PCLK;
ATIM_InitStructure.CounterAlignedMode = ATIM_COUNT_MODE_EDGE_ALIGN;
ATIM_InitStructure.CounterDirection = ATIM_COUNTING_UP;
ATIM_InitStructure.CounterOPMode = ATIM_OP_MODE_REPETITIVE;
ATIM_InitStructure.OverFlowMask = DISABLE;
ATIM_InitStructure.Prescaler = ATIM_Prescaler_DIV1;
ATIM_InitStructure.ReloadValue = 6400; // 10KHz
ATIM_InitStructure.RepetitionCounter = 0;
ATIM_InitStructure.UnderFlowMask = DISABLE;
ATIM_Init(&ATIM_InitStructure);
ATIM_OCInitStructure.BufferState = DISABLE; // 比较缓存使能状态
ATIM_OCInitStructure.OCDMAState = DISABLE; // 比较匹配触发DMA使能状态
ATIM_OCInitStructure.OCInterruptSelect = ATIM_OC_IT_UP_COUNTER; // 比较匹配触发中断使能状态
ATIM_OCInitStructure.OCInterruptState = ENABLE; // 比较匹配触发中断使能状态
ATIM_OCInitStructure.OCMode = ATIM_OCMODE_PWM1; // 比较模式配置
ATIM_OCInitStructure.OCPolarity = ATIM_OCPOLARITY_NONINVERT; // 端口极性选择:正向、反向---不翻转电平
ATIM_OC1AInit(&ATIM_OCInitStructure); // CH1A比较输出设置
ATIM_OC1BInit(&ATIM_OCInitStructure); // CH1B比较输出设置
ATIM_ITConfig(ATIM_CR_IT_OVE, ENABLE); // ATIM中断设置
ATIM_CH1Config(ATIM_CHxB_CIE | ATIM_CHxA_CIE, ENABLE); // 设置通道1的功能
ATIM_SetCompare1A(3200); // 50%占空比
ATIM_SetCompare1B(2560); // 40%占空比
// ATIM_PWMOutputConfig(OCREFA_TYPE_SINGLE, OUTPUT_TYPE_COMP, 1); // 设置PWM输出的参数
ATIM_Cmd(ENABLE); // ATIM 启动
ATIM_CtrlPWMOutputs(ENABLE); // 使能PWM输出
}callback.c
/*
* @function: ATIM_IRQHandler
* @param: None
* @retval: None
* @brief: 高级定时器中断服务函数
*/
void ATIM_IRQHandler(void)
{
if (ATIM_GetITStatus(ATIM_IT_OVF))
{
ATIM_ClearITPendingBit(ATIM_IT_OVF);
}
if (ATIM_GetITStatus(ATIM_IT_C1BF))
{
ATIM_ClearITPendingBit(ATIM_IT_C1BF);
}
if (ATIM_GetITStatus(ATIM_IT_C1AF))
{
ATIM_ClearITPendingBit(ATIM_IT_C1AF);
}
}TFT-ST7735
见【屏幕学习篇】
OLED
见【协议学习篇】
ADC
- 手册
一个12 位精度ADC
16 路输入转换通道
–13 路外部引脚输入
–内置温度传感器
–内置 BGR 1.2V 基准
–1/3 VDDA 电源电压4 路参考电压源(Vref)
–VDDA 电源电压
–ExRef(PB0)引脚电压
–内置 1.5V 参考电压
–内置 2.5V 参考电压
只有一个ADC所以不能同时进行单通道或者序列
- 硬件连接
- 程序编写
单通道单次采集 + 序列扫描采集(多通道)
adc1.h
#ifndef __ADC1_H
#define __ADC1_H
#include "main.h"
typedef struct
{
volatile uint8_t gFlagIrq; // ADC完成采集中断标志位
volatile float ADC_Single_Result; // ADC单次采集单通道转换结果
volatile float ADC_Serial_Result_Arr[4]; // ADC序列扫描转换结果数组
void (*ADC1_Single_Channel_One_Init)(void); // ADC单通道单次采集初始化
void (*ADC1_Serial_Scan_Init)(void); // ADC序列扫描采集初始化
void (*ADC1_Single_Channel_One_Convert)(void); // ADC单通道单次采集转换
void (*ADC1_Serial_Scan_Convert)(void); // ADC序列扫描采集转换
}ADC_1_t;
extern ADC_1_t ADC_1;
#endifadc1.c
/***************************************************************************
* File: adc1.c
* Author: Luckys.
* Date: 2023/06/15
* description: ADC
-----------------------------------
注意:板子的电位器,对应ADC通道引脚是PB0,然后还添加了另外3个通道分别是PA4 PA5 PA6
注意:需要在【public.h】进行ADC选择模式(单通道单次/序列扫描)
-----------------------------------
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void ADC1_Single_Channel_One_Init(void); // ADC单通道单次采集初始化
static void ADC1_Serial_Scan_Init(void); // ADC序列扫描采集初始化
static void ADC1_Single_Channel_One_Convert(void); // ADC单通道单次采集转换
static void ADC1_Serial_Scan_Convert(void); // ADC序列扫描采集转换
/*====================================static function declaration area END====================================*/
ADC_1_t ADC_1 =
{
0,
0,
{0.0},
ADC1_Single_Channel_One_Init,
ADC1_Serial_Scan_Init,
ADC1_Single_Channel_One_Convert,
ADC1_Serial_Scan_Convert,
};
/*
* @function: ADC1_Single_Channel_One_Init
* @param: None
* @retval: None
* @brief: ADC单通道单次采集初始化
*/
static void ADC1_Single_Channel_One_Init(void)
{
ADC_SingleChTypeDef ADC_SingleChInitStructure;
// 打开时钟
__RCC_GPIOB_CLK_ENABLE();
__RCC_ADC_CLK_ENABLE();
// 引脚设为模拟输入
PB00_ANALOG_ENABLE(); // PB0 --- ADC_CH8
/*ADC配置*/
ADC_SingleChInitStructure.ADC_Chmux = ADC_ExInputCH8; // 输入通道 -- 8
ADC_SingleChInitStructure.ADC_DiscardEn = ADC_DiscardNull; // 单通道ADC转换结果保存策略配置
ADC_SingleChInitStructure.ADC_WdtStruct.ADC_WdtAll = ADC_WdtDisable; // ADC模拟看门狗使能 -- 不使能
ADC_SingleChInitStructure.ADC_InitStruct.ADC_AccEn = ADC_AccDisable; // 转换结果累加是否使能 -- 否
ADC_SingleChInitStructure.ADC_InitStruct.ADC_Align = ADC_AlignRight; // 转换结果对齐方式 -- 右对齐(采集多少就是多少)
ADC_SingleChInitStructure.ADC_InitStruct.ADC_ClkDiv = ADC_Clk_Div4; // 时钟选择(ADC工作时钟ADCCLK,由系统时钟PCLK 经预分频器分频得到) -- 4分频
ADC_SingleChInitStructure.ADC_InitStruct.ADC_DMAEn = ADC_DmaDisable; // ADC转换完成是/否触发DMA使能 -- 不使能
ADC_SingleChInitStructure.ADC_InitStruct.ADC_InBufEn = ADC_BufEnable; // ADC输入增益使能 -- 开启
ADC_SingleChInitStructure.ADC_InitStruct.ADC_OpMode = ADC_SingleChOneMode; // 操作模式 -- 单通道单次转换模式
ADC_SingleChInitStructure.ADC_InitStruct.ADC_SampleTime = ADC_SampTime10Clk; // ADC采样时间 -- 10个ADCCLK 个数
ADC_SingleChInitStructure.ADC_InitStruct.ADC_TsEn = ADC_TsDisable; // 内置温度传感器是/否使能 -- 否
ADC_SingleChInitStructure.ADC_InitStruct.ADC_VrefSel = ADC_Vref_VDDA; // ADC参考电压 -- 使用芯片供电电压
ADC_SingleChOneModeCfg(&ADC_SingleChInitStructure); // ADC单通道单次转换模式配置
ADC_Enable(); // ADC使能
ADC_SoftwareStartConvCmd(ENABLE); // ADC转换软件启动
}
/*
* @function: ADC1_Serial_Scan_Init
* @param: None
* @retval: None
* @brief: ADC序列扫描采集初始化
*/
static void ADC1_Serial_Scan_Init(void)
{
ADC_SerialChTypeDef ADC_SerialChInitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 打开时钟
__RCC_GPIOA_CLK_ENABLE();
__RCC_GPIOB_CLK_ENABLE();
__RCC_ADC_CLK_ENABLE();
// 引脚设为模拟输入
PA04_ANALOG_ENABLE(); // PA4 --- ADC_CH4
PA05_ANALOG_ENABLE(); // PA5 --- ADC_CH5
PA06_ANALOG_ENABLE(); // PA6 --- ADC_CH6
PB00_ANALOG_ENABLE(); // PB0 --- ADC_CH8
ADC_StructInit(&ADC_InitStructure); // 默认值初始化
ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div16; // ADC CLK: 64000000/16 = 4000KHz 不能超过24M
ADC_SerialChInitStructure.ADC_SqrEns = ADC_SqrEns03; // 采集4个通道
ADC_SerialChInitStructure.ADC_Sqr0Chmux = ADC_SqrCh8;
ADC_SerialChInitStructure.ADC_Sqr1Chmux = ADC_SqrCh4;
ADC_SerialChInitStructure.ADC_Sqr2Chmux = ADC_SqrCh5;
ADC_SerialChInitStructure.ADC_Sqr3Chmux = ADC_SqrCh6;
ADC_SerialChInitStructure.ADC_InitStruct = ADC_InitStructure;
ADC_SerialChScanModeCfg(&ADC_SerialChInitStructure); // 序列扫描转换初始化
ADC_ITConfig(ADC_IT_EOS, ENABLE); // 中断使能
ADC_EnableIrq(ADC_INT_PRIORITY); // 优先级
ADC_ClearITPendingAll(); // 清除所有ADC中断标志
ADC_Enable(); // ADC使能
ADC_SoftwareStartConvCmd(ENABLE); // ADC转换软件启动
}
/*
* @function: ADC1_Single_Channel_One_Convert
* @param: None
* @retval: None
* @brief: ADC单通道单次采集转换
*/
static void ADC1_Single_Channel_One_Convert(void)
{
uint16_t adc_temp;
ADC_SoftwareStartConvCmd(ENABLE);
while(ADC_GetITStatus(ADC_IT_EOC))
{
ADC_ClearITPendingBit(ADC_IT_EOC); // 清除标志位
adc_temp = ADC_GetConversionValue(); // 获取单次转换的值
ADC_1.ADC_Single_Result = (float)adc_temp*(3.3f/4096u); // 计算电压结果
}
}
/*
* @function: ADC1_Serial_Scan_Convert
* @param: None
* @retval: None
* @brief: ADC序列扫描采集转换
*/
static void ADC1_Serial_Scan_Convert(void)
{
uint16_t adc_temp[4];
while (!(ADC_1.gFlagIrq & ADC_ISR_EOS_Msk)); // &上掩码0x2
ADC_1.gFlagIrq = 0u; // 置0
ADC_GetSqr0Result(adc_temp); // 获取序列0通道转换的值
ADC_GetSqr1Result(&adc_temp[1]); // 获取序列1通道转换的值
ADC_GetSqr2Result(&adc_temp[2]); // 获取序列2通道转换的值
ADC_GetSqr3Result(&adc_temp[3]); // 获取序列3通道转换的值
ADC_SoftwareStartConvCmd(ENABLE); // ADC转换软件启动
ADC_1.ADC_Serial_Result_Arr[0] = (float)adc_temp[0] * (3.3f / 4096u); // 计算电压结果--PB0
ADC_1.ADC_Serial_Result_Arr[1] = (float)adc_temp[1] * (3.3f / 4096u); // 计算电压结果--PA4
ADC_1.ADC_Serial_Result_Arr[2] = (float)adc_temp[2] * (3.3f / 4096u); // 计算电压结果--PA5
ADC_1.ADC_Serial_Result_Arr[3] = (float)adc_temp[3] * (3.3f / 4096u); // 计算电压结果--PA6
}
callback.c
/*
* @function: ADC_IRQHandler
* @param: None
* @retval: None
* @brief: ADC中断服务函数
*/
void ADC_IRQHandler(void)
{
ADC_1.gFlagIrq = CW_ADC->ISR; // 获取中断标志寄存器
CW_ADC->ICR = 0x00; // 中断标志清除寄存器
}这次采集4个通道的值,注意复用功能
RTC
- 手册
初始化一次后,后面的初始化是改变了RTC的,复位的话它底层也会判断,所以想改RTC时间的话则需要使用函数
RTC_SetTime和RTC_SetDate进行更改(注意需要这个结构体的全部成员都初始化一遍比如24小时制那些否则默认是12小时制的)一开始我还去改底层那个,其实不用改这样默认就挺好的,需要改时间的话就用函数
设置日期和时间时需要设置为BCD码格式,即每4个二进制表示一个十进制数,比如十进制16对应BCD码就是0x16(相当于十进制前面加0x而已)
闹钟功能:
下面是屏蔽标志,当你选择
RTC_AlarmMask_All后,相当于秒中断了 cpp// 如果想屏蔽所有,只剩下秒可以这样,这样就每分钟的第10s就触发了 RTC_AlarmMask_All & (~RTC_AlarmMask_Seconds); // 如果想加上分钟限制可以这样 RTC_AlarmMask_All & (~(RTC_AlarmMask_Seconds|RTC_AlarmMask_Minutes)); // 其他以此类推
- 程序编写
rtc.h
#ifndef __RTC_H
#define __RTC_H
#include "main.h"
typedef enum
{
YEAR = (uint8_t)0,
MON = (uint8_t)1,
DAY = (uint8_t)2,
HOUR = (uint8_t)3,
MIN = (uint8_t)4,
SEC = (uint8_t)5,
}myRTC_Buff_Index_t;
typedef struct
{
uint16_t usRtc_Buff[6]; // 存储年月日时分秒
void (*myRTC_Init)(uint16_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); // RTC初始化
void (*myRTC_Refresh)(void); // RTC刷新
void (*myRTC_Set_Time)(uint8_t, uint8_t, uint8_t); // 设置时间
void (*myRTC_Set_Date)(uint16_t, uint8_t, uint8_t,uint8_t); // 设置日期
void (*myRTC_Alarm_A_Init)(void); // 闹钟A初始化
}myRTC_t;
extern myRTC_t myRTC;
#endifrtc.c
/***************************************************************************
* File: rtc.c
* Author: Luckys.
* Date: 2023/06/15
* description: RTC
****************************************************************************/
#include "main.h"
/*====================================variable definition declaration area BEGIN===================================*/
// RTC时间日期存储结构体
RTC_TimeTypeDef nTime;
RTC_DateTypeDef nDate;
// 星期
static uint8_t *WeekdayStr[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
/*====================================variable definition declaration area END===================================*/
/*====================================static function declaration area BEGIN====================================*/
static void myRTC_Init(uint16_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
static void myRTC_Calculate_Date(uint16_t, uint8_t, uint8_t*); // RTC日期计算(十进制 to BCD码)
static void myRTC_Calculate_Time(uint8_t, uint8_t, uint8_t, uint8_t*); // RTC时间计算(十进制 to BCD码)
static void myRTC_Refresh(void); // RTC刷新
static void myRTC_Set_Time(uint8_t, uint8_t, uint8_t); // 设置RTC时间
static void myRTC_Set_Date(uint16_t, uint8_t, uint8_t,uint8_t); // 设置RTC日期
static void myRTC_Alarm_A_Init(void); // 闹钟A初始化
/*====================================static function declaration area END====================================*/
myRTC_t myRTC =
{
{0},
myRTC_Init,
myRTC_Refresh,
myRTC_Set_Time,
myRTC_Set_Date,
myRTC_Alarm_A_Init,
};
/*
* @function: myRTC_Init
* @param: 年 月(RTC_Month_June) 日 星期(RTC_Weekday_Friday) 时 分 秒
* @retval: None
* @brief: RTC初始化
*/
static void myRTC_Init(uint16_t year, uint8_t mon, uint8_t day, uint8_t week, uint8_t hour, uint8_t min, uint8_t sec)
{
RTC_InitTypeDef RTC_InitStructure;
uint8_t Date_temp[2] = {0}; // 按顺序存储: 【年,日】
uint8_t Time_temp[3] = {0}; // 按顺序存储: 【时,分,秒】
__RCC_RTC_CLK_ENABLE();
RCC_LSE_Enable(RCC_LSE_MODE_OSC, RCC_LSE_AMP_NORMAL, RCC_LSE_DRIVER_NORMAL); // 选择LSE为RTC时钟
myRTC_Calculate_Date(year,day,Date_temp); // 计算日期
myRTC_Calculate_Time(hour,min,sec,Time_temp); // 计算时间
// 设置日期,DAY、MONTH、YEAR必须为BCD方式,星期为0~6,代表星期日,星期一至星期六
RTC_InitStructure.DateStruct.Year = Date_temp[0]; // 【年】
RTC_InitStructure.DateStruct.Month = mon; // 【月】
RTC_InitStructure.DateStruct.Day = Date_temp[1]; // 【日】
RTC_InitStructure.DateStruct.Week = week; // 【星期】
// 打印测试
printf("-----Set Date as 20%x/%x/%x\r\n",RTC_InitStructure.DateStruct.Year, RTC_InitStructure.DateStruct.Month, RTC_InitStructure.DateStruct.Day);
// 设置时间,HOUR、MINIUTE、SECOND必须为BCD方式,须保证HOUR、AMPM、H24之间的关联正确性
RTC_InitStructure.TimeStruct.Hour = Time_temp[0]; // 【时】
RTC_InitStructure.TimeStruct.Minute = Time_temp[1]; // 【分】
RTC_InitStructure.TimeStruct.Second = Time_temp[2]; // 【秒】
RTC_InitStructure.TimeStruct.AMPM = 0;
RTC_InitStructure.TimeStruct.H24 = 1; // 24小时制(1) 12小时制(0) -- 24小时
RTC_InitStructure.RTC_ClockSource = RTC_RTCCLK_FROM_LSE; // 时钟源选择 -- 外部晶体时钟LSE
// 打印测试
printf("-----Set Time as %02x:%02x:%02x\r\n",RTC_InitStructure.TimeStruct.Hour,RTC_InitStructure.TimeStruct.Minute,RTC_InitStructure.TimeStruct.Second);
RTC_Init(&RTC_InitStructure);
#ifdef USE_RTC_Interrupt
RTC_SetInterval(RTC_INTERVAL_EVERY_1S); // RTC秒中断
RTC_ITConfig(RTC_IT_INTERVAL,ENABLE); // 开启RTC中断
NVIC_EnableIRQ(RTC_IRQn); // 开启RTC中断
#endif
}
/*
* @function: myRTC_Alarm_A_Init
* @param: None
* @retval: None
* @brief: 闹钟A初始化
*/
static void myRTC_Alarm_A_Init(void)
{
RTC_AlarmTypeDef RTC_AlarmStructure;
RTC_AlarmStructure.RTC_AlarmMask = RTC_AlarmMask_All & (~(RTC_AlarmMask_Seconds|RTC_AlarmMask_Minutes|RTC_AlarmMask_Hours)); // 时分秒吻合触发
RTC_AlarmStructure.RTC_AlarmTime.AMPM = 0;
RTC_AlarmStructure.RTC_AlarmTime.H24 = 1;
RTC_AlarmStructure.RTC_AlarmTime.Hour = 0x19; // 设置闹钟晚上7点触发
RTC_AlarmStructure.RTC_AlarmTime.Minute = 0x00;
RTC_AlarmStructure.RTC_AlarmTime.Second = 0x00;
RTC_SetAlarm(RTC_Alarm_A,&RTC_AlarmStructure); // 设置闹钟
RTC_AlarmCmd(RTC_Alarm_A,ENABLE); // 使能闹钟
RTC_ITConfig(RTC_IT_ALARMA,ENABLE); // 开启闹钟中断
NVIC_EnableIRQ(RTC_IRQn); // 开启RTC中断
}
/*
* @function: myRTC_Calculate_Date
* @param: 年 日 存储结果的数组
* @retval: None
* @brief: RTC日期计算(十进制 to BCD码)
*/
static __inline void myRTC_Calculate_Date(uint16_t year, uint8_t day, uint8_t* p_temp)
{
*(p_temp + 0) = ((year / 1000) << 4) | ((year / 100) % 10) | ((year / 10) % 10) << 4 | (year % 10); // 【年】
*(p_temp + 1) = ((day / 10) % 10) << 4 | (day % 10); // 【日】
}
/*
* @function: myRTC_Calculate_Time
* @param: 时 分 秒 存储结果的数组
* @retval: None
* @brief: RTC时间计算(十进制 to BCD码)
*/
static __inline void myRTC_Calculate_Time(uint8_t hour, uint8_t min, uint8_t sec, uint8_t* p_temp)
{
*(p_temp + 0) = ((hour / 10) % 10) << 4 | (hour % 10); // 【时】
*(p_temp + 1) = ((min / 10) % 10) << 4 | (min % 10); // 【分】
*(p_temp + 2) = ((sec / 10) % 10) << 4 | (sec % 10); // 【秒】
}
/*
* @function: myRTC_Refresh
* @param: None
* @retval: None
* @brief: RTC刷新
*/
static void myRTC_Refresh(void)
{
RTC_GetDate(&nDate); // 获取日期
RTC_GetTime(&nTime); // 获取时间
// OLED刷新
sprintf((char *)Page2.OLED096_Display_Buff[0], "Date:20%02x/%02x/%02x", nDate.Year, nDate.Month, nDate.Day); // 日期
OLED096.padString((char *)Page2.OLED096_Display_Buff[0], 16); // 补全空格
sprintf((char *)Page2.OLED096_Display_Buff[1], "Time:%02x-%02x-%02x", nTime.Hour, nTime.Minute, nTime.Second); // 时间
OLED096.padString((char *)Page2.OLED096_Display_Buff[1], 16); // 补全空格
sprintf((char *)Page2.OLED096_Display_Buff[2], "Week:%s", WeekdayStr[nDate.Week]); // 星期
OLED096.padString((char *)Page2.OLED096_Display_Buff[2], 16); // 补全空格
}
/*
* @function: myRTC_Set_Time
* @param: 时分秒
* @retval: None
* @brief: 设置RTC时间
*/
static void myRTC_Set_Time(uint8_t hour, uint8_t min, uint8_t sec)
{
RTC_TimeTypeDef sTime;
uint8_t Time_temp[3] = {0};
myRTC_Calculate_Time(hour,min,sec,Time_temp); // 计算
sTime.Hour = Time_temp[0];
sTime.Minute = Time_temp[1];
sTime.Second = Time_temp[2];
sTime.AMPM = 0;
sTime.H24 = 1;
RTC_SetTime(&sTime); // 设置时间
}
/*
* @function: myRTC_Set_Date
* @param: None
* @retval: None
* @brief: 设置RTC日期
*/
static void myRTC_Set_Date(uint16_t year, uint8_t mon, uint8_t day,uint8_t week)
{
RTC_DateTypeDef sDate;
uint8_t Date_temp[2] = {0};
myRTC_Calculate_Date(year,day,Date_temp); // 计算
sDate.Year = Date_temp[0];
sDate.Month = mon;
sDate.Day = Date_temp[1];
sDate.Week = week;
RTC_SetDate(&sDate); // 设置日期
}
callback.c
/*
* @function: RTC_IRQHandler
* @param: None
* @retval: None
* @brief: RTC中断服务函数
*/
void RTC_IRQHandler(void)
{
if (RTC_GetITState(RTC_IT_ALARMA)) // 闹钟中断触发
{
RTC_ClearITPendingBit(RTC_IT_ALARMA);
Buzzer.Buzzer_ON();
}
#ifdef USE_RTC_Interrupt
if (RTC_GetITState(RTC_IT_INTERVAL)) // RTC秒中断触发
{
RTC_ClearITPendingBit(RTC_IT_INTERVAL);
}
#endif
}DHT11温湿度
- 硬件连接
-
介绍可以看【STM32入门篇】
-
时序
复位与检测DHT11
读取数据0/1
- 程序编写
dht11.h
#ifndef __DHT11_H
#define __DHT11_H
#include "main.h"
// 引脚定义 PB1
#define DHT11_Pin GPIO_PIN_1
// 引脚模式设置(输出/输入)
#define DHT11_PIN_OUT PB01_DIR_OUTPUT()
#define DHT11_PIN_INPUT PB01_DIR_INPUT()
// 引脚拉低/拉高
#define DHT11_PIN_RESET PB01_SETLOW()
#define DHT11_PIN_SET PB01_SETHIGH()
// 读取引脚电平
#define DHT11_Read_Pin GPIO_ReadPin(CW_GPIOB,DHT11_Pin)
typedef struct
{
float DHT11_Temperture; // 温度
uint8_t DHT11_Humidity; // 湿度
uint8_t (*DHT11_Init)(void); // DHT11初始化
uint8_t (*DHT11_Read_Data)(float *, uint8_t *); // DNT11获取一次数据
}DHT11_t;
extern DHT11_t DHT11;
#endifdht11.c
/***************************************************************************
* File: dht11.c
* Author: Luckys.
* Date: 2023/06/16
* description: dht11温湿度传感器
-----------------------------------
接线:
GND ---- GND
3.3V ---- 3.3V
PB1 ---- S
-----------------------------------
****************************************************************************/
#include "main.h"
/*====================================static function declaration area BEGIN====================================*/
static void DHT11_Rest(void); // DHT11复位
static uint8_t DHT11_Init(void); // DHT11初始化
static uint8_t DHT11_Check(void); // DHT11检测
static uint8_t DHT11_Read_Bit(void); // DHT11读取一个位
static uint8_t DHT11_Read_Byte(void); // DHT11读取一个字节
static uint8_t DHT11_Read_Data(float *, uint8_t *); // DHT11读取一次数据
/*====================================static function declaration area END====================================*/
DHT11_t DHT11 =
{
0.0,
0,
DHT11_Init,
DHT11_Read_Data,
};
/*
* @function: DHT11_Rest
* @param: None
* @retval: None
* @brief: DHT11复位
*/
static void DHT11_Rest(void)
{
DHT11_PIN_OUT; // 输出
DHT11_PIN_RESET; // 拉低
Public.System_MS_Delay(20); // 拉低至少18ms
DHT11_PIN_SET; // 拉高
Public.System_10us_Delay(3); // 拉高至少20~40us
}
/*
* @function: DHT11_Check
* @param: None
* @retval: FALSE -- 未检测到DHT11存在 TRUE -- 存在
* @brief: DHT11检测
*/
static uint8_t DHT11_Check(void)
{
uint8_t retry = TRUE;
DHT11_PIN_INPUT; // 输入
while (DHT11_Read_Pin && (retry < 10)) // DHT11会拉低40~80us
{
retry++;
Public.System_10us_Delay(1);
}
if (retry >= 10)
{
return FALSE;
}
else
{
retry = TRUE;
}
while ((!DHT11_Read_Pin) && (retry < 10)) // DHT11拉低后会再拉高40~80us
{
retry++;
Public.System_10us_Delay(1);
}
if (retry >= 10)
{
return FALSE;
}
else
{
return TRUE;
}
}
/*
* @function: DHT11_Read_Bit
* @param: None
* @retval: 1/0
* @brief: DHT11读取一个位
*/
static uint8_t DHT11_Read_Bit(void)
{
uint8_t retry = 0;
while (DHT11_Read_Pin && (retry < 10)) // 等待变成低电平
{
retry++;
Public.System_10us_Delay(1);
}
retry = 0;
while ((!DHT11_Read_Pin) && (retry < 10)) // 等待变成高电平
{
retry++;
Public.System_10us_Delay(1);
}
Public.System_10us_Delay(4); // 进入高电平后延时40us(取一个中间值)
if (DHT11_Read_Pin) // 判断高低电平,即数据1或0
{
return 1;
}
else
{
return 0;
}
}
/*
* @function: DHT11_Read_Byte
* @param: None
* @retval: 读到的数据
* @brief: DHT11读取一个字节
*/
static uint8_t DHT11_Read_Byte(void)
{
uint8_t i, dat;
dat = 0;
for (i = 0; i < 8; i++)
{
dat <<= 1;
dat |= DHT11_Read_Bit();
}
return dat;
}
/*
* @function: DHT11_Read_Data
* @param: None
* @retval: TRUE -- 成功 FALSE -- 失败
* @brief: DHT11读取一次数据
*/
static uint8_t DHT11_Read_Data(float *temp,uint8_t *humi)
{
char buf[5];
uint8_t i;
DHT11_Rest(); // 复位
if (DHT11_Check()) // 检测成功
{
for (i = 0; i < 5; i++) // 读取5个数据
{
buf[i] = DHT11_Read_Byte();
}
if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) // 湿度整数-湿度小数-温度整数-温度小数
{
*humi = buf[0];
*temp = buf[2] + (float)buf[3] / 10.0f; // 合并为一个完整的小数
// printf("A:%d B:%d C:%d D:%d\r\n",buf[0],buf[1],buf[2],buf[3]);
// OLED刷新
sprintf((char*)Page1.OLED096_Display_Buff[0],"Temp:%.1f",*temp);
OLED096.padString((char*)Page1.OLED096_Display_Buff[0],8);
sprintf((char*)Page1.OLED096_Display_Buff[1],"Humidity:%d %%",*humi);
OLED096.padString((char*)Page1.OLED096_Display_Buff[1],16);
}
}
else
{
return FALSE;
}
return TRUE;
}
/*
* @function: DHT11_Init
* @param: None
* @retval: DHT11的回应 TRUE -- 成功 FALSE -- 失败
* @brief: DHT11初始化
*/
static uint8_t DHT11_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__RCC_GPIOB_CLK_ENABLE();
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_InitStructure.Pins = DHT11_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
DHT11_Rest(); // 复位
return DHT11_Check(); // 等待DHT11回应
}
光敏电阻
直接把引脚接到ADC通道即可,然后正常ADC电压计算输出
DMA
不常用
基本配置
cppDMA_InitTypeDef DMA_InitStructure; __RCC_DMA_CLK_ENABLE(); DMA_StructInit(&DMA_InitStructure); // 结构体初始化 DMA_InitStructure.DMA_DstAddress = (uint32_t) &ADC_1.ADC_Result_Array[1]; // 目标地址 DMA_InitStructure.DMA_DstInc = DMA_DstAddress_Increase; // 指定目标地址寄存器是否递增 -- 递增 DMA_InitStructure.DMA_Mode = DMA_MODE_BLOCK; // 操作模式 -- BLOCK 每传输完成 1 个数据块后会插入一个传输间隙 DMA_InitStructure.DMA_SrcAddress = (uint32_t) &ADC_1.ADC_Result_Array[0]; // 源地址 DMA_InitStructure.DMA_SrcInc = DMA_SrcAddress_Fix; // 指定源地址寄存器是否递增 -- 不递增 DMA_InitStructure.DMA_TransferCnt = 16; // DMA传输次数 DMA_InitStructure.DMA_TransferWidth = DMA_TRANSFER_WIDTH_32BIT; // 数据位宽 -- 32位 DMA_InitStructure.HardTrigSource = DMA_HardTrig_ADC_TRANSCOMPLETE; // ADC转换完成触发 -- 硬触发 DMA_InitStructure.TrigMode = DMA_HardTrig; // 触发模式 -- 硬触发
更新记录
| 时间 | 备注 |
|---|---|
| 2023/6/23 | 提交最终版 |
常见问题
以下问题大部分来自群大佬总结,我把一些会遇到的记录在这!
如果取模.h报错,有可能是编码问题,汉字的话需要把.h保存为GB2312格式保存
CW32要用AC5编译器,用AC6编译会报错(怎么解决可以看【Keil相关】文章)
- 编译报找不到assert_failed的错误
多见于自己新建的工程,两个解决方法:
- main.c中添加
cpp#ifdef USE_FULL_ASSERT void assert_failed(uint8_t *file, uint32_t line) { } #endif
- 去
base_types.h注释掉下面这行 cpp#define USE_FULL_ASSERT
出现这个报错,是因为编译器不识别
inline这种写法,解决方法是把inline替换为__inline即可
- 如果烧录时提示找不到FLM文件
解决方法:
- 可以手动复制FLM文件到这个文件夹,安装完pack文件后flm文件可以在这个位置找到:
- 把原来的算法文件删掉重新添加,列表里找不到CW32的请确认是否安装了pack





















































