蓝桥杯物联网竞赛_STM32L071KBU6_第十五届蓝桥杯物联网竞赛国赛代码解析
目录
- 前言:
- 1、配置及代码:
- (1) A板:
- [1] CUBMX配置:
- [2] keil5代码:
- (2) B板:
- [1] CUBMX配置:
- [2] keil5代码:
- 2、代码解析:
- 3、总结:
前言:
这届国赛是目前为止最难一届,我赛后完完整整又做了一遍,花了我两天大概12小时,这个难度完全可以当一个stm32的期末设计,下面将对本届的题结合对应代码做重点解析
1、配置及代码:
(1) A板:
[1] CUBMX配置:
1、DMA串口配置:
2、TIM3配置:
3、TIM7定时器配置:
[2] keil5代码:
main.c
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include "Function.h"
#include "oled.h"
#include "lora.h"
/* 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_I2C3_Init();
MX_TIM3_Init();
MX_USART2_UART_Init();
MX_SPI1_Init();
MX_TIM7_Init();
/* USER CODE BEGIN 2 */
Function_OledInit(50);
LORA_Init();
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_3);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Function_MyMain();
}
/* USER CODE END 3 */
}
Function.c
#include "Function.h"
#include "main.h"
#include "oled.h"
#include "i2c.h"
#include "spi.h"
#include "lora.h"
#include
#include
#include "usart.h"
#include "tim.h"
#include
unsigned char URXMSG[20];
unsigned char UTXMSG[20];
unsigned char LTXMSG[20];
unsigned char LRXMSG[20];
int BEIGNNUMBER = 0;
int ENDNUMBER = 0;
unsigned char STATETIM = 0;
unsigned char ARRAY1[20];
int PLUSEVALUE = 0;
uint16_t COUNTNUMBER = 0;
unsigned char MEMORDER[20] = {'N', 'F', ' '};
void OLED_Write(unsigned char type, unsigned char data){
unsigned char WriteData[2];
WriteData[0] = type;
WriteData[1] = data;
HAL_I2C_Master_Transmit(&hi2c3, 0x78, WriteData, 2, 0xff);
}
void Function_OledInit(unsigned char ms){
HAL_GPIO_WritePin(OLED_Power_GPIO_Port, OLED_Power_Pin, GPIO_PIN_RESET);
HAL_Delay(ms);
OLED_Init();
}
unsigned char SPI_WriteRead(unsigned char address, unsigned char data){
unsigned char TxData[2], RxData[2];
TxData[0] = address;
TxData[1] = data;
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, TxData, RxData, sizeof(TxData), 0xff);
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET);
return RxData[1];
}
void Function_ArrayClean(unsigned char* array, uint16_t len){
for(unsigned char i = 0; i < len; i ++) array[i] = '\0';
}
void myprintf(unsigned char address, const char* format, ...) {
char ARRAY[40];
// 使用va_list和va_start来处理可变参数
va_list args;
va_start(args, format);
// 使用vsprintf将可变参数列表格式化的字符串存储到ARRAY中
vsprintf(ARRAY, format, args);
// 清理可变参数列表
va_end(args);
// 假设OLED_ShowString函数可以在OLED屏幕上显示字符串
OLED_ShowString(address, (unsigned char*)ARRAY);
}
void Function_UartTxOk(){
HAL_UART_Transmit(&huart2, (unsigned char* )"OK ", 5, 0xff);
}
void Function_UartTxError(){
HAL_UART_Transmit(&huart2, (unsigned char* )"ERROR ", 8, 0xff);
}
void Function_HandleURxMsg(){
if(URXMSG[0] == '$' && URXMSG[1] == '\0'){
LORA_Tx((unsigned char* )"$", 1);
}
if(URXMSG[0] == '?' && URXMSG[1] == '\0'){
LORA_Tx((unsigned char* )"?", 1);
}
if(URXMSG[0] == '@' && URXMSG[1] == '\0'){
sprintf((char* )LTXMSG, "%d", PLUSEVALUE);
LORA_Tx(LTXMSG, 10);
Function_ArrayClean(LTXMSG, sizeof(LTXMSG));
}
if(URXMSG[0] == 'F'){
if(strncmp((char* )URXMSG, "FS1:", 4) == 0 || strncmp((char* )URXMSG, "FS2:", 4) == 0) LORA_Tx(URXMSG, 20); // 判断是不是正确的
else Function_UartTxError();
}
unsigned char temp = URXMSG[0];
if(temp == '$' && URXMSG[1] == '\0'){ // 切换和同步命令需要返还OK
Function_UartTxOk(); // 返还OK
Function_ArrayClean(URXMSG, sizeof(URXMSG)); // 清理
}
if(((temp == '?' || temp == '@') && URXMSG[1] == '\0') || temp == 'F'){ // 参数和查询指令交给B端处理后统一返还
Function_ArrayClean(URXMSG, sizeof(URXMSG));
}
if(URXMSG[0] != '\0') { // 接收了其他垃圾消息
Function_ArrayClean(URXMSG, sizeof(URXMSG));
Function_UartTxError(); // 返还错误
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ // 10ms
if(COUNTNUMBER < 300) COUNTNUMBER ++;
else{
COUNTNUMBER = 0;
HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_SET); // 随手关灯
}
}
void Function_LoRxMsg(){ // B端处理后的结果
LORA_Rx(LRXMSG);
if(LRXMSG[0] != '\0'){
if(LRXMSG[0] == '0') Function_UartTxError(); // A端接收的数据不正确
else if(LRXMSG[0] == 'R' || LRXMSG[0] == 'N'){ // 接收到LORB传递的数据
HAL_UART_Transmit(&huart2, LRXMSG, strlen((char* )LRXMSG), 0xff);
HAL_TIM_Base_Start_IT(&htim7); // 开启定时器
HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_RESET); // 开灯
}
else if(LRXMSG[0] == '1') Function_UartTxOk(); // A端接收的数据正确
Function_ArrayClean(LRXMSG, sizeof(LRXMSG)); // 清除接收的数据
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
//OLED_ShowString(0, "ok");
if(STATETIM == 0){
STATETIM ++;
BEIGNNUMBER = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_3);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_3, TIM_ICPOLARITY_FALLING);
}else if(STATETIM == 1){
STATETIM ++;
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_3, TIM_ICPOLARITY_RISING);
}else if(STATETIM == 2){
STATETIM ++;
ENDNUMBER = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_3);
}
}
void Function_UartRxMsg(){
HAL_UARTEx_ReceiveToIdle_IT(&huart2, URXMSG, 50);
if(URXMSG[0] != '\0'){
HAL_Delay(10); // 让cpu等待DMA把数据接收完
memcpy(MEMORDER, URXMSG, 20);
Function_HandleURxMsg();
}
}
void Function_PluseShow(){
if(STATETIM == 3){
PLUSEVALUE = (int)(1000000 * 1.0 / (ENDNUMBER - BEIGNNUMBER));
myprintf(2, " %dHZ ", PLUSEVALUE);
//myprintf(0, " %d ", ENDNUMBER - BEIGNNUMBER);
//myprintf(2, " %.1f ", (ENDNUMBER - BEIGNNUMBER) / 10.0);
STATETIM = 0;
HAL_Delay(200);
}
}
void Function_MyMain(){
Function_UartRxMsg();
//myprintf(0, URXMSG);
myprintf(0, " %s ", MEMORDER); // 显示最近一次接收到的数据
Function_LoRxMsg();
Function_PluseShow();
//Function_HandleURxMsg();
}
Function.h
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
#include "main.h"
void OLED_Write(unsigned char type, unsigned char data);
void Function_OledInit(unsigned char ms);
void Function_MyMain();
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif
(2) B板:
[1] CUBMX配置:
1、PWM方波配置:
2、TIM7定时器配置:
[2] keil5代码:
main.c
#include "main.h"
#include "adc.h"
#include "i2c.h"
#include "spi.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include "Function.h"
#include "oled.h"
#include "lora.h"
/* 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_I2C3_Init();
MX_SPI1_Init();
MX_TIM2_Init();
MX_TIM7_Init();
MX_ADC_Init();
/* USER CODE BEGIN 2 */
Function_OledInit(50);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 开启方波 1kHZ
HAL_TIM_Base_Start_IT(&htim7);
LORA_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Function_MyMain();
}
/* USER CODE END 3 */
}
Function.c
#include "Function.h"
#include "main.h"
#include "i2c.h"
#include "oled.h"
#include
#include "tim.h"
#include "adc.h"
#include
#include "spi.h"
#include "lora.h"
#include
#include
float FS1 = 6.0;
float FS2 = 6.0;
float F1VL = 0;
float F2VL = 0;
unsigned char STATEADC = 0; // ADC状态
unsigned char OLEDSHOW = 0; // OLED显示
uint16_t COUNTNUM = 0;
unsigned char LEDSTATE = 0;
float DERTF1F2 = 0; // 获取FS1与FS2差值 / 0.2
unsigned char INDEX = 7; // 算一下AA中第一个A的具体位置
unsigned char MEMWINNER[20] = {'N', 'F', ' '};
uint16_t ADCRUNNUM = 0;
unsigned char RUNSHOWARR[20];
unsigned char LORXMSG[20];
int NEWPWM = 0;
int CLK = 32000000;
int MAINDRT = 32000000;
int INDEXX = 0;
int INDEXY = 0; // 获取要调的参数
void OLED_Write(unsigned char type, unsigned char data){
unsigned char WriteData[2];
WriteData[0] = type;
WriteData[1] = data;
HAL_I2C_Master_Transmit(&hi2c3, 0x78, WriteData, 2, 0xff);
}
void Function_OledInit(unsigned char ms){
HAL_GPIO_WritePin(OLED_Power_GPIO_Port, OLED_Power_Pin, GPIO_PIN_RESET);
HAL_Delay(ms);
OLED_Init();
}
unsigned char SPI_WriteRead(unsigned char address, unsigned char data){
unsigned char TxData[2], RxData[2];
TxData[0] = address;
TxData[1] = data;
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, TxData, RxData, sizeof(TxData), 0xff);
HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET);
return RxData[1];
}
void myprintf(unsigned char address, const char* format, ...) {
char ARRAY[40];
// 使用va_list和va_start来处理可变参数
va_list args;
va_start(args, format);
// 使用vsprintf将可变参数列表格式化的字符串存储到ARRAY中
vsprintf(ARRAY, format, args);
// 清理可变参数列表
va_end(args);
// 假设OLED_ShowString函数可以在OLED屏幕上显示字符串
OLED_ShowString(address, (unsigned char*)ARRAY);
}
void Function_ArrayClean(unsigned char* array, uint16_t len){
for(unsigned char i = 0; i < len; i ++) array[i] = '\0';
}
void Function_GetAdc(){
uint16_t AdcData[2];
float AdcValue[2];
for(unsigned char i = 0; i < 2; i ++){
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 0xff);
AdcData[i] = HAL_ADC_GetValue(&hadc);
AdcValue[i] = AdcData[i] * 3.30f / 4095;
}
HAL_ADC_Stop(&hadc);
F1VL = FS1 / 3.0 * AdcValue[1] - 0.1 * FS1;
F2VL = FS2 / 3.0 * AdcValue[0] - 0.1 * FS1;
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ // 按键
//OLED_ShowString(0, "we");
OLEDSHOW = (OLEDSHOW + 1) % 3;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ // 10ms中断一次
/* 为run状态服务 */
if(STATEADC == 2){
if(ADCRUNNUM % 20 == 0) Function_GetAdc(); // 按题意理解采集器在run状态才会采集且采集间隔为0.2s
ADCRUNNUM ++;
// 更新A的坐标
if(F1VL >= F2VL){
DERTF1F2 = (int)((F1VL - F2VL) / 0.2); // 记得要强转int一下,注意(int)的优先级
if(DERTF1F2 >= 7) INDEX = 0; // 更新A的坐标
else INDEX = 7 - DERTF1F2;
}else{
DERTF1F2 = (int)((F2VL - F1VL) / 0.2);
if(DERTF1F2 + 7 >= 14) INDEX = 14;
else INDEX = 7 + DERTF1F2;
}
if(INDEX == 0 || INDEX == 14){ // 判断AA是否在左右两边
if(COUNTNUM < 500) COUNTNUM ++;
else{ // 坚持5s,记录获胜者
COUNTNUM = 0;
ADCRUNNUM = 0;
STATEADC = 0;
if(INDEX == 0) sprintf((char* )MEMWINNER, "RP1:%.1f ", F1VL);
else sprintf((char* )MEMWINNER, "RP2:%.1f ", F2VL);
LEDSTATE = 1; // 有人获胜开启LD5
}
}else COUNTNUM = 0; // AA没在边缘不记时
}
/* 为LED闪烁服务 */
if(LEDSTATE == 1){
if(COUNTNUM < 500){
if(COUNTNUM % 10 == 0) HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin); // 0.1s也就是100ms,整10倍数闪烁一次即可
COUNTNUM ++;
}else{
COUNTNUM = 0;
LEDSTATE = 0;
HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_SET); // 将LD5灯关掉
}
}
}
void Function_RunStateShow(){
for(unsigned char i = 0; i < 16; i ++) RUNSHOWARR[i] = '#';
RUNSHOWARR[INDEX] = 'A';
RUNSHOWARR[INDEX + 1] = 'A'; // 填充
myprintf(0, " RUN ");
myprintf(2, (char* )RUNSHOWARR);
}
void Function_OledShow(){
if(OLEDSHOW == 0){
if(STATEADC == 0){
myprintf(0, " Idle ");
myprintf(2, "#######AA#######");
}else if(STATEADC == 1){
myprintf(0, " Ready ");
myprintf(2, "#######AA#######");
}else if(STATEADC == 2){
Function_RunStateShow();
}
}else if(OLEDSHOW == 1){
myprintf(0, " F1:%.1fKN ", F1VL);
myprintf(2, " F2:%.1fKN ", F2VL);
}else if(OLEDSHOW == 2){
myprintf(0, " FS1:%.1fKN ", FS1);
myprintf(2, " FS2:%.1fKN ", FS2);
//myprintf(2, " kkkk ");
}
}
void Function_PwmChange(){ // 传过来的PWM不一定能整除,选择最接近的PWM
NEWPWM = (int)strtof((char* )LORXMSG, NULL); // 获取要配置的新的PWM
MAINDRT = CLK;
for(int i = 1; i <= 65535; i ++){
for(int j = 1; j <= 65535; j ++){
if(i * j * NEWPWM - CLK > MAINDRT) break; // 差值太大说明j太大
int temp = abs(i * j * NEWPWM - CLK);
if(temp <= MAINDRT){ // 如果差值很小
MAINDRT = temp;
INDEXX = i;
INDEXY = j;
}
}
if(MAINDRT == 0) break; // 能整除就没必要继续了
}
__HAL_TIM_SET_PRESCALER(&htim2, INDEXX - 1);
__HAL_TIM_SET_AUTORELOAD(&htim2, INDEXY - 1); // 修改预分频与自动重装载
//myprintf(0, "MIN: %d ", MAINDRT);
//myprintf(2, "NPM: %d ",CLK / INDEXX / INDEXY);
//myprintf(2, "%d %d", INDEXX, INDEXY);
}
void Function_LoRxMsg(){
LORA_Rx(LORXMSG);
//myprintf(0, LORXMSG);
if(LORXMSG[0] == '$'){ // 切换模式
STATEADC = (STATEADC + 1) % 3;
//myprintf(0, "%d", STATEADC);
}
if(LORXMSG[0] == '?'){ // 发送上一次的胜利者
LORA_Tx(MEMWINNER, sizeof(MEMWINNER));
}
if(LORXMSG[0] >= '0' && LORXMSG[0] <= '9'){ // 传回了脉冲信号
//myprintf(2, "%d", STATEADC);
if(STATEADC == 0){
Function_PwmChange();
LORA_Tx((unsigned char* )"1", 1);
}
else LORA_Tx((unsigned char* )"0", 1); // 传回报错信息
}
if(LORXMSG[0] == 'F'){ // 修改命令
unsigned char memfs[20];
memcpy(memfs, LORXMSG + 4, 10);
char* q = NULL;
float temp = strtof((char* )memfs, &q);
if(*q != '\0') LORA_Tx((unsigned char* )"0", 1); // 错误数据
else if(LORXMSG[2] == '1'){
FS1 = temp;
LORA_Tx((unsigned char* )"1", 1);
}else if(LORXMSG[2] == '2'){
FS2 = temp;
LORA_Tx((unsigned char* )"1", 1); // 返回成功信息
}
//myprintf(0, "temp: %.1f", temp);
//myprintf(2, "F1: %.1f F2: %.1f", FS1, FS2);
}
if(LORXMSG[0] != '\0') Function_ArrayClean(LORXMSG, sizeof(LORXMSG));
}
void Function_K1K2Ctrl(){
if(STATEADC == 0){ // 空闲
HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_RESET);
}else if(STATEADC == 1){
HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_RESET);
}else if(STATEADC == 2){
HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_SET);
}
}
void Function_MyMain(){
Function_LoRxMsg();
Function_K1K2Ctrl();
Function_OledShow();
//myprintf(0, "%d,%c,%s,%.1fH", 20, 'a', "hello", 11.1);
}
Function.h
#ifndef __FUNCTION_H__
#define __FUNCTION_H__
#include "main.h"
void OLED_Write(unsigned char type, unsigned char data);
void Function_OledInit(unsigned char ms);
void Function_MyMain();
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void myprintf(unsigned char address, const char* format, ...);
unsigned char SPI_WriteRead(unsigned char address, unsigned char data);
#endif
2、代码解析:
下面将对比赛中的难点进行着重分析:
由题意可以看出来,设备采集器应是独立于OLED显示的,也就是说OLED显示的界面不影响设备采集器的状态,同样设备采集器只会在OLED出现对应显示的时候才会对其做特定影响
怎样分开两者呢?
这里可以将设备采集器放在定时器里,而oled显示放在主函数里,首先题意明确了设备采集器的采集间隔为0.2s,获胜条件为AA在左右两端保持5s,有获胜者还要LD5闪烁5s,所以说毫无疑问可以将这些过程全部放在定时器做处理,刚好定时器是中断处理,与主函数分开
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ // 10ms中断一次
/* 为run状态服务 */
if(STATEADC == 2){
if(ADCRUNNUM % 20 == 0) Function_GetAdc(); // 按题意理解采集器在run状态才会采集且采集间隔为0.2s
ADCRUNNUM ++;
// 更新A的坐标
if(F1VL >= F2VL){
DERTF1F2 = (int)((F1VL - F2VL) / 0.2); // 记得要强转int一下,注意(int)的优先级
if(DERTF1F2 >= 7) INDEX = 0; // 更新A的坐标
else INDEX = 7 - DERTF1F2;
}else{
DERTF1F2 = (int)((F2VL - F1VL) / 0.2);
if(DERTF1F2 + 7 >= 14) INDEX = 14;
else INDEX = 7 + DERTF1F2;
}
if(INDEX == 0 || INDEX == 14){ // 判断AA是否在左右两边
if(COUNTNUM < 500) COUNTNUM ++;
else{ // 坚持5s,记录获胜者
COUNTNUM = 0;
ADCRUNNUM = 0;
STATEADC = 0;
if(INDEX == 0) sprintf((char* )MEMWINNER, "RP1:%.1f ", F1VL);
else sprintf((char* )MEMWINNER, "RP2:%.1f ", F2VL);
LEDSTATE = 1; // 有人获胜开启LD5
}
}else COUNTNUM = 0; // AA没在边缘不记时
}
/* 为LED闪烁服务 */
if(LEDSTATE == 1){
if(COUNTNUM < 500){
if(COUNTNUM % 10 == 0) HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin); // 0.1s也就是100ms,整10倍数闪烁一次即可
COUNTNUM ++;
}else{
COUNTNUM = 0;
LEDSTATE = 0;
HAL_GPIO_WritePin(LD5_GPIO_Port, LD5_Pin, GPIO_PIN_SET); // 将LD5灯关掉
}
}
}
定时器一直保持着开启,通过接收A板的信息来改变STATEADC的值,进入定时器后就能做相应操作
动态页面的实现逻辑其实就是填充往########
填充AA
,所以只需记录A的下标即可,显示的时候直接根据下标填充显示
// 更新A的坐标
if(F1VL >= F2VL){
DERTF1F2 = (int)((F1VL - F2VL) / 0.2); // 记得要强转int一下,注意(int)的优先级
if(DERTF1F2 >= 7) INDEX = 0; // 更新A的坐标
else INDEX = 7 - DERTF1F2;
}else{
DERTF1F2 = (int)((F2VL - F1VL) / 0.2);
if(DERTF1F2 + 7 >= 14) INDEX = 14;
else INDEX = 7 + DERTF1F2;
}
更新部分是在定时器里更新的,填充两个A只需要记录左边一个A的下标即可右边一个A的下标就是index + 1
void Function_RunStateShow(){
for(unsigned char i = 0; i < 16; i ++) RUNSHOWARR[i] = '#';
RUNSHOWARR[INDEX] = 'A';
RUNSHOWARR[INDEX + 1] = 'A'; // 填充
myprintf(0, " RUN ");
myprintf(2, (char* )RUNSHOWARR);
}
void Function_OledShow(){
if(OLEDSHOW == 0){
if(STATEADC == 0){
myprintf(0, " Idle ");
myprintf(2, "#######AA#######");
}else if(STATEADC == 1){
myprintf(0, " Ready ");
myprintf(2, "#######AA#######");
}else if(STATEADC == 2){
Function_RunStateShow();
}
}else if(OLEDSHOW == 1){
myprintf(0, " F1:%.1fKN ", F1VL);
myprintf(2, " F2:%.1fKN ", F2VL);
}else if(OLEDSHOW == 2){
myprintf(0, " FS1:%.1fKN ", FS1);
myprintf(2, " FS2:%.1fKN ", FS2);
//myprintf(2, " kkkk ");
}
}
显示部分由两个参数控制一个是OLEDSHOW这个参数是由按键控制其值,STATADC由B端发送的数据控制
串口发送的数据给A板,让A板对数据做检验或初步检验,减轻B板数据处理的负担,A板检验后的数据B板能直接用数据,B板处理后又能回复给A板
脉冲同步,比较麻烦因为B端要同步A端脉冲不一定能完美的同步因为32M / PSC / ARR不一定等于PWM
那就只能设计一个算法让他近似等于了
void Function_PwmChange(){ // 传过来的PWM不一定能整除,选择最接近的PWM
NEWPWM = (int)strtof((char* )LORXMSG, NULL); // 获取要配置的新的PWM
MAINDRT = CLK;
for(int i = 1; i <= 65535; i ++){
for(int j = 1; j <= 65535; j ++){
if(i * j * NEWPWM - CLK > MAINDRT) break; // 差值太大说明j太大
int temp = abs(i * j * NEWPWM - CLK);
if(temp <= MAINDRT){ // 如果差值很小
MAINDRT = temp;
INDEXX = i;
INDEXY = j;
}
}
if(MAINDRT == 0) break; // 能整除就没必要继续了
}
__HAL_TIM_SET_PRESCALER(&htim2, INDEXX - 1);
__HAL_TIM_SET_AUTORELOAD(&htim2, INDEXY - 1); // 修改预分频与自动重装载
//myprintf(0, "MIN: %d ", MAINDRT);
//myprintf(2, "NPM: %d ",CLK / INDEXX / INDEXY);
//myprintf(2, "%d %d", INDEXX, INDEXY);
}
设计算法找到让同步的结果和预期最接近的PSC和ARR
3、总结:
总结来说这次比赛难点在于功能复杂,导致难设计,以及代码量的增加