前言
本次工训赛时间太紧没有及时完成吗,但是在解决问题的过程中,自己创建了很多的算法代码,用于调试,本篇文章用于保存这些代码。
基本中断配置
定时器中断以及串口中断的代码定义函数
其中定时器为5ms执行一次
HAL_TIM_Base_Start_IT(&htim2);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time=0;
time++;
if(time%5000==0)
led1_toggle();
if (htim->Instance == TIM2)
{
usart1_time(1);
uart3_time(1);
uart5_time(1);
}
if (htim->Instance == TIM4)
{
}
}
串口中断配置
因为进中断后都要进入中断回调函数,但是中断回调函数只有一个,所以要分串口进入
int fputc(int ch, FILE *f)
{
//HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,1000);//锟斤拷锟酵达拷锟斤拷
return ch;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
USART1_HANDLE();
}
if(huart->Instance == UART4)
{
UART4_HANDLE();
}
if (huart->Instance == USART2)
{
UART2_HANDLE();
}
if (huart->Instance == USART3)
{
USART3_HANDLE();
}
if (huart->Instance == UART5)
{
USART5_HANDLE();
}
}
串口接收不定时长框架代码
下面的.c和.h文件,是我的串口1,主要是用来调试的,我装了个串口蓝牙,然后使用printf()在上位机来观察并且调试
interaction.c
#include "interaction.h"
#include "string.h"
#include "stdio.h"
#include "led.h"
#include "k210ewm.h"
#include "lift.h"
uint8_t ineraction_buffer[1];
char ineraction_OUT[100],send_buff[100];
void ineraction_str_change()
{
uint16_t count = 0;
char *p;
p=ineraction_OUT;
while (*p!='\0')
{
count=count*10+(*p-'0');
p++;
}
debug_counrt(1,count);
printf("count=%d\r\n",count);
}
uint16_t debug_counrt(uint16_t cmd,uint16_t num)
{
static uint16_t count=0;
switch (cmd)
{
case 0:
{
return count;
break;
}
case 1:
count=num;
break;
}
}
void interaction_init(void)
{
HAL_UART_Receive_IT(&huart1, (uint8_t *)ineraction_buffer, 1);
}
void USART1_HANDLE()
{
ineraction_OUT[uart1_length_chang(0)]=ineraction_buffer[0];
uart1_length_chang(1);
ineraction_change_flag(1);
HAL_UART_Receive_IT(&huart1, (uint8_t *)ineraction_buffer, 1);
}
uint16_t usart1_time(uint8_t cmd)
{
static uint16_t time=0;
switch (cmd)
{
case 0:
return time;
break;
case 1:
time++;
if ((time>10)&&ineraction_change_flag(0))
{
usart1_time(2);
ineraction_change_flag(2);
ineraction_str_change();
memset(ineraction_OUT, '\0', sizeof(ineraction_OUT));
uart1_length_chang(2);
}
break;
case 2:
time=0;
break;
}
}
uint8_t ineraction_change_flag(uint8_t cmd)
{
static uint8_t flag=0;
switch (cmd)
{
case 0:
return flag;
break;
case 1:
flag=1;
break;
case 2:
flag=0;
break;
}
}
uint16_t uart1_length_chang(uint8_t cmd)
{
static uint16_t length=0;
switch (cmd)
{
case 0:
return length;
break;
case 1:
length++;
break;
case 2:
length=0;
break;
}
}
interaction.h
#ifndef INTERACTION_H
#define INTERACTION_H
#include "usart.h"
uint16_t usart1_time(uint8_t cmd);
uint8_t ineraction_change_flag(uint8_t cmd);
uint16_t uart1_length_chang(uint8_t cmd);
uint16_t debug_counrt(uint16_t cmd,uint16_t num);
void USART1_HANDLE();
void interaction_init(void);
#endif
按键模块
按键直接用阻塞式就好了,按键主要也是用于调试
key.c
#include "key.h"
uint8_t key_return()
{
uint8_t k = 0;
// 检测 PD8 引脚
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_8) == GPIO_PIN_RESET)
{
HAL_Delay(20);
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_8) == GPIO_PIN_RESET)
{
while (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_8) == GPIO_PIN_RESET);
k = 1;
}
HAL_Delay(20);
}
// 检测 PD9 引脚
else if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_9) == GPIO_PIN_RESET)
{
HAL_Delay(20);
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_9) == GPIO_PIN_RESET)
{
while (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_9) == GPIO_PIN_RESET);
k = 2;
}
HAL_Delay(20);
}
// 检测 PD10 引脚
else if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_10) == GPIO_PIN_RESET)
{
HAL_Delay(20);
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_10) == GPIO_PIN_RESET)
{
while (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_10) == GPIO_PIN_RESET);
k = 3;
}
HAL_Delay(20);
}
// 检测 PD11 引脚
else if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_11) == GPIO_PIN_RESET)
{
HAL_Delay(20);
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_11) == GPIO_PIN_RESET)
{
while (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_11) == GPIO_PIN_RESET);
k = 4;
}
HAL_Delay(20);
}
return k;
}
key.h
#ifndef KEY
#define KEY
#include "gpio.h"
uint8_t key_return();
#endif
舵机
舵机我单独封装
其中psc=主频*10
arr=2000
Servo.c
#include "Servo.h"
void Servo_Init()
{
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
Servo_SetAngle(100);
}
void Servo_SetAngle( uint16_t angle)
{
uint32_t pulse_width;
if (angle > 120)
{
angle = 120;
}
else if (angle < 20)
{
angle = 20;
}
pulse_width = 50 + (angle * (250 - 50) / 180);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, pulse_width);
}
void clip_open()
{
Servo_SetAngle(125);
}
void clip_close()
{
Servo_SetAngle(20);
}
void clip_wait()
{
Servo_SetAngle(60);
}
void clip_release()
{
Servo_SetAngle(20);
uint8_t i=20;
while (i++!=80)
{
Servo_SetAngle(i);
HAL_Delay(5);
}
}
Servo.h
#ifndef SERVO_H
#define SERVO_H
#include "tim.h"
void Servo_Init(void);
void Servo_SetAngle(uint16_t angle);
void clip_open();
void clip_close();
void clip_wait();
void clip_release();
#endif
存储芯片
还以为是跟电赛一样不能带电脑去调,于是自己编写调试程序,调试代码,结果可以带电脑调,嘤嘤嘤...
是使用w25k64,我前面的文章也有
int32_t save_digital(uint8_t flag, uint16_t address, int32_t num);
主要是使用这个函数实现读写,具体自己看.c文件
w25q64_soft_spi.c
#include "w25q64_soft_spi.h"
#include <string.h>
// 每个扇区大小为4KB
#define SECTOR_SIZE 4096
// 软件SPI初始化
void W25Q64_SoftSPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置CS引脚
GPIO_InitStruct.Pin = W25Q64_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(W25Q64_CS_PORT, &GPIO_InitStruct);
// 配置CLK引脚
GPIO_InitStruct.Pin = W25Q64_CLK_PIN;
HAL_GPIO_Init(W25Q64_CLK_PORT, &GPIO_InitStruct);
// 配置MOSI引脚
GPIO_InitStruct.Pin = W25Q64_MOSI_PIN;
HAL_GPIO_Init(W25Q64_MOSI_PORT, &GPIO_InitStruct);
// 配置MISO引脚
GPIO_InitStruct.Pin = W25Q64_MISO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(W25Q64_MISO_PORT, &GPIO_InitStruct);
// 片选信号初始化为高电平
W25Q64_CS_HIGH();
}
// 软件SPI读写一个字节
uint8_t W25Q64_SoftSPI_ReadWriteByte(uint8_t byte)
{
uint8_t receivedByte = 0;
for (int i = 7; i >= 0; i--)
{
if (byte & (1 << i))
{
W25Q64_MOSI_HIGH();
}
else
{
W25Q64_MOSI_LOW();
}
W25Q64_CLK_LOW();
HAL_Delay(1); // 适当延时,确保时钟信号稳定
receivedByte <<= 1;
if (W25Q64_READ_MISO())
{
receivedByte |= 0x01;
}
W25Q64_CLK_HIGH();
HAL_Delay(1);
}
return receivedByte;
}
// 读取设备ID
void W25Q64_ReadID(uint8_t * ManufacturerID, uint16_t * DeviceID)
{
W25Q64_CS_LOW();
uint8_t temp = W25Q64_SoftSPI_ReadWriteByte(0x90); // 发送读ID指令
temp = W25Q64_SoftSPI_ReadWriteByte(0x00);
temp = W25Q64_SoftSPI_ReadWriteByte(0x00);
temp = W25Q64_SoftSPI_ReadWriteByte(0x00);
*ManufacturerID = W25Q64_SoftSPI_ReadWriteByte(0xFF);
*DeviceID = W25Q64_SoftSPI_ReadWriteByte(0xFF) << 8;
*DeviceID |= W25Q64_SoftSPI_ReadWriteByte(0xFF);
W25Q64_CS_HIGH();
}
// 写使能
void W25Q64_WriteEnable(void)
{
W25Q64_CS_LOW();
W25Q64_SoftSPI_ReadWriteByte(0x06); // 发送写使能指令
W25Q64_CS_HIGH();
}
// 等待写操作结束
void W25Q64_WaitForWriteEnd(void)
{
uint8_t status;
W25Q64_CS_LOW();
W25Q64_SoftSPI_ReadWriteByte(0x05); // 发送读状态寄存器指令
do
{
status = W25Q64_SoftSPI_ReadWriteByte(0xFF);
} while (status & 0x01);
W25Q64_CS_HIGH();
}
// 扇区擦除
void W25Q64_SectorErase(uint32_t SectorAddr)
{
W25Q64_WriteEnable();
W25Q64_CS_LOW();
W25Q64_SoftSPI_ReadWriteByte(0x20); // 发送扇区擦除指令
W25Q64_SoftSPI_ReadWriteByte((SectorAddr >> 16) & 0xFF);
W25Q64_SoftSPI_ReadWriteByte((SectorAddr >> 8) & 0xFF);
W25Q64_SoftSPI_ReadWriteByte(SectorAddr & 0xFF);
W25Q64_CS_HIGH();
W25Q64_WaitForWriteEnd();
}
// 页编程
void W25Q64_PageProgram(uint32_t Address, uint8_t * pBuffer, uint16_t NumByteToWrite)
{
W25Q64_WriteEnable();
W25Q64_CS_LOW();
W25Q64_SoftSPI_ReadWriteByte(0x02); // 发送页编程指令
W25Q64_SoftSPI_ReadWriteByte((Address >> 16) & 0xFF);
W25Q64_SoftSPI_ReadWriteByte((Address >> 8) & 0xFF);
W25Q64_SoftSPI_ReadWriteByte(Address & 0xFF);
for (uint16_t i = 0; i < NumByteToWrite; i++)
{
W25Q64_SoftSPI_ReadWriteByte(pBuffer[i]);
}
W25Q64_CS_HIGH();
W25Q64_WaitForWriteEnd();
}
// 缓冲区读取
void W25Q64_BufferRead(uint32_t Address, uint8_t * pBuffer, uint16_t NumByteToRead)
{
W25Q64_CS_LOW();
W25Q64_SoftSPI_ReadWriteByte(0x03); // 发送读数据指令
W25Q64_SoftSPI_ReadWriteByte((Address >> 16) & 0xFF);
W25Q64_SoftSPI_ReadWriteByte((Address >> 8) & 0xFF);
W25Q64_SoftSPI_ReadWriteByte(Address & 0xFF);
for (uint16_t i = 0; i < NumByteToRead; i++)
{
pBuffer[i] = W25Q64_SoftSPI_ReadWriteByte(0xFF);
}
W25Q64_CS_HIGH();
}
#include "w25q64_soft_spi.h"
// 假设SECTOR_SIZE在头文件或者其他地方已经定义
#define SECTOR_SIZE 4096
int32_t save_digital(uint8_t flag, uint16_t address, int32_t num)
{
// 定义一个错误返回值,这里用 -1 作为错误标识
const int32_t ERROR_RETURN = -1;
if (address > 2047) {
return ERROR_RETURN; // 地址超出范围,返回错误值
}
uint32_t sectorAddr = address * SECTOR_SIZE;
uint8_t buffer[4]; // 用于存储32位数据的字节数组
int32_t result = 0;
switch (flag) {
case 0: // 写入
buffer[0] = (num >> 24) & 0xFF;
buffer[1] = (num >> 16) & 0xFF;
buffer[2] = (num >> 8) & 0xFF;
buffer[3] = num & 0xFF;
W25Q64_SectorErase(sectorAddr); // 先擦除扇区
W25Q64_PageProgram(sectorAddr, buffer, 4); // 写入数据
break;
case 1: // 读取
W25Q64_BufferRead(sectorAddr, buffer, 4);
result = ((int32_t)buffer[0] << 24) | ((int32_t)buffer[1] << 16) | ((int32_t)buffer[2] << 8) | buffer[3];
break;
case 2: // 清除
W25Q64_SectorErase(sectorAddr);
break;
default:
result = ERROR_RETURN; // 无效的标志位,返回错误值
break;
}
return result;
}
Servo.h
#ifndef __W25Q64_SOFT_SPI_H
#define __W25Q64_SOFT_SPI_H
#include "gpio.h"
// 定义软件SPI引脚
#define W25Q64_CS_PIN GPIO_PIN_14
#define W25Q64_CS_PORT GPIOC
#define W25Q64_CLK_PIN GPIO_PIN_13
#define W25Q64_CLK_PORT GPIOC
#define W25Q64_MISO_PIN GPIO_PIN_15
#define W25Q64_MISO_PORT GPIOC
#define W25Q64_MOSI_PIN GPIO_PIN_4
#define W25Q64_MOSI_PORT GPIOA
// 片选操作宏定义
#define W25Q64_CS_LOW() HAL_GPIO_WritePin(W25Q64_CS_PORT, W25Q64_CS_PIN, GPIO_PIN_RESET)
#define W25Q64_CS_HIGH() HAL_GPIO_WritePin(W25Q64_CS_PORT, W25Q64_CS_PIN, GPIO_PIN_SET)
// 时钟操作宏定义
#define W25Q64_CLK_LOW() HAL_GPIO_WritePin(W25Q64_CLK_PORT, W25Q64_CLK_PIN, GPIO_PIN_RESET)
#define W25Q64_CLK_HIGH() HAL_GPIO_WritePin(W25Q64_CLK_PORT, W25Q64_CLK_PIN, GPIO_PIN_SET)
// MOSI操作宏定义
#define W25Q64_MOSI_LOW() HAL_GPIO_WritePin(W25Q64_MOSI_PORT, W25Q64_MOSI_PIN, GPIO_PIN_RESET)
#define W25Q64_MOSI_HIGH() HAL_GPIO_WritePin(W25Q64_MOSI_PORT, W25Q64_MOSI_PIN, GPIO_PIN_SET)
// 读取MISO引脚电平
#define W25Q64_READ_MISO() HAL_GPIO_ReadPin(W25Q64_MISO_PORT, W25Q64_MISO_PIN)
// 函数声明
void W25Q64_SoftSPI_Init(void);
uint8_t W25Q64_SoftSPI_ReadWriteByte(uint8_t byte);
void W25Q64_ReadID(uint8_t * ManufacturerID, uint16_t * DeviceID);
void W25Q64_WriteEnable(void);
void W25Q64_WaitForWriteEnd(void);
void W25Q64_SectorErase(uint32_t SectorAddr);
void W25Q64_PageProgram(uint32_t Address, uint8_t * pBuffer, uint16_t NumByteToWrite);
void W25Q64_BufferRead(uint32_t Address, uint8_t * pBuffer, uint16_t NumByteToRead);
int32_t save_digital(uint8_t flag, uint16_t address, int32_t num);
#endif
灯两灭调试
这个没什么好说的,就是测试而已
led.c
#include "led.h"
#include "gpio.h"
void led1_on(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
}
void led1_off(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
}
void led2_on(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);
}
void led2_off(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);
}
void led3_on(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
}
void led3_off(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
}
void led1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_0);
}
void led2_toggle(void)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_1);
}
void led3_toggle(void)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
}
led.h
#ifndef LED_H
#define LED_H
void led1_on(void);
void led1_off(void);
void led2_on(void);
void led2_off(void);
void led3_on(void);
void led3_off(void);
void led1_toggle(void);
void led2_toggle(void);
#endif
维特科技陀螺仪
初始化后直接获取就好
在前面的文章中我有讲的
wtkj.c
#include "wtkj.h"
#include "stdio.h"
uint8_t buffer[1];
float xyz_return(uint8_t number,float count)
{
static float xyz[3]={0};
if(number>2)
{
xyz[number-3]=count;
return 0;
}
else
{
return xyz[number];
}
}
void xyz_init(void)
{
// 修改为串口2
HAL_UART_Receive_IT(&huart2, (uint8_t *)buffer, 1);
}
void UART2_HANDLE(void)
{
static uint8_t i = 0;
static int16_t buff[8];
buff[i++] = buffer[0];
if((i == 1) && (buff[0] != 0x55))
{
i = 0;
}
if((i == 2) && (buff[1] != 0x53))
{
i = 0;
}
if(i == 8)
{
int16_t value_x = (buff[3] << 8) | buff[2];
int16_t value_y = (buff[5] << 8) | buff[4];
int16_t value_z = (buff[7] << 8) | buff[6];
xyz_return(3,(float)value_x / 32768* 180) ;
xyz_return(4,(float)value_y / 32768* 180) ;
xyz_return(5,(float)value_z / 32768* 180) ;
for(int j = 0; j < 8; j++)
{
buff[j] = 0;
}
i = 0;
}
// 修改为串口2
HAL_UART_Receive_IT(&huart2, (uint8_t *)buffer, 1);
}
wtkj.h
#ifndef WTKJ
#define WTKJ
#include "usart.h"
float xyz_return(uint8_t number,float count);
void xyz_init(void);
// 修改为串口2的处理函数名,假设是UART2_HANDLE
void UART2_HANDLE(void);
#endif
串口屏的使用
我这里使用的是3.5寸淘晶驰T1系列无铁框串口屏,在串口屏设置好后,发送什么就显示什么
lcd.c
#include "lcd.h"
#include "usart.h"
#include "string.h"
#include "stdio.h"
// 定义缓冲区和输出数组
uint8_t lcd_buffer[1];
char lcd_OUT[100], lcd_buff[100];
// 字符串处理函数
void lcd_str_change()
{
uart4_send_data(lcd_OUT, strlen(lcd_OUT));
memset(lcd_OUT, '\0', sizeof(lcd_OUT));
uart4_length_chang(2);
}
// 标志位处理函数
uint8_t lcd_change_flag(uint8_t cmd)
{
static uint8_t flag = 0;
switch (cmd)
{
case 0:
return flag;
case 1:
flag = 1;
break;
case 2:
flag = 0;
break;
}
return 0;
}
// 串口4中断处理函数
void UART4_HANDLE(void)
{
lcd_OUT[uart4_length_chang(0)] = lcd_buffer[0];
uart4_length_chang(1);
lcd_change_flag(1);
uart4_time(2);
HAL_UART_Receive_IT(&huart4, (uint8_t *)lcd_buffer, 1);
}
// 串口长度处理函数
uint16_t uart4_length_chang(uint8_t cmd)
{
static uint16_t length = 0;
switch (cmd)
{
case 0:
return length;
case 1:
length++;
break;
case 2:
length = 0;
break;
}
return 0;
}
// 初始化函数
void lcd_init(void)
{
HAL_UART_Receive_IT(&huart4, (uint8_t *)lcd_buffer, 1);
}
// 串口发送函数
void uart4_send_data(const char *data, int length)
{
HAL_UART_Transmit(&huart4, (uint8_t *)data, length, HAL_MAX_DELAY);
}
// 时间处理函数
uint16_t uart4_time(uint8_t cmd)
{
static uint16_t time = 0;
switch (cmd)
{
case 0:
return time;
case 1:
time++;
if ((time > 10) && lcd_change_flag(0))
{
uart4_time(2);
lcd_change_flag(2);
lcd_str_change();
}
break;
case 2:
time = 0;
break;
}
return 0;
}
lcd.h
#ifndef LCD_H
#define LCD_H
#include <stdint.h>
// 定义缓冲区和输出数组
extern uint8_t lcd_buffer[1];
extern char lcd_OUT[100], lcd_buff[100];
// 函数声明
void lcd_str_change();
uint8_t lcd_change_flag(uint8_t cmd);
void UART4_HANDLE(void);
uint16_t uart4_length_chang(uint8_t cmd);
void lcd_init(void);
void uart4_send_data(const char *data, int length);
uint16_t uart4_time(uint8_t cmd);
#endif
传入多少就会变成多少的算法
这个是步进电机的算法,因为步进电机加上编码器闭环,所以输入多少就会指定多少,那么就根据这个原理来写函数,用来调试要升到多高
void lift_chassis_H_direction(uint16_t targe)//1上升 0下降
{
static uint16_t high=0;
if(targe>1200)
return;
if(targe>high)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_SET);
while (high!=targe)
{
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_7);
HAL_Delay(1);
high++;
}
printf("hight=%d\r\n",high);
}
else if (targe<high)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET);
while (high!=targe)
{
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_7);
HAL_Delay(1);
high--;
}
printf("hight=%d\r\n",high);
}
}