前言

在这个实验项目中,我使用STM32CubeMX配置了基础硬件,实现了PWM呼吸灯效果、DMA接收串口数据、ADC双通道测电压等功能。经过一番调试,最终成功实现了所有功能,同时也解决了一些遇到的问题。下面将详细介绍整个实验的过程和注意事项。

基础配置

开启时钟

LCD

这里不要忘了开启PB0

开启串口1,添加DMA

TIM


因为PB5接的是LED灯,所以直接选择PB5,如果自动选不会选PB5。

ADC



配置完,生成代码

代码编写

测试

首先测试一下各个功能是否正常

将我之前写的库导入,将多余的代码删去,首先编译一下。

接下来测试串口和LED

DMA接收

uart.c

1
2
3
volatile uint8_t rx_len = 0;  //接收一帧数据的长度
volatile uint8_t recv_end_flag = 0; //一帧数据接收完成标志
uint8_t rx_buffer[100]={0}; //接收数据缓存数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void MX_USART1_UART_Init(void)
{

huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
//下方为自己添加的代码
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断

//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);


}

uart.h

1
2
3
4
5
6
7
8
9
10
extern UART_HandleTypeDef huart1;
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
/* USER CODE BEGIN Private defines */


#define BUFFER_SIZE 100
extern volatile uint8_t rx_len ; //接收一帧数据的长度
extern volatile uint8_t recv_end_flag; //一帧数据接收完成标志
extern uint8_t rx_buffer[100]; //接收数据缓存数组

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
*********************************************************************************************************
* 函 数 名: DMA_Usart_Send
* 功能说明: 串口发送功能函数
* 形 参: buf,len
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA_Usart_Send(uint8_t *buf,uint8_t len)//串口发送封装
{
if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
{
Error_Handler();
}

}



/*
*********************************************************************************************************
* 函 数 名: DMA_Usart1_Read
* 功能说明: 串口接收功能函数
* 形 参: Data,len
* 返 回 值: 无
*********************************************************************************************************
*/
void DMA_Usart1_Read(uint8_t *Data,uint8_t len)//串口接收封装
{
HAL_UART_Receive_DMA(&huart1,Data,len);//重新打开DMA接收
}

while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
if(recv_end_flag == 1) //接收完成标志
{


DMA_Usart_Send(rx_buffer, rx_len);
rx_len = 0;//清除计数
recv_end_flag = 0;//清除接收结束标志位
// for(uint8_t i=0;i<rx_len;i++)
// {
// rx_buffer[i]=0;//清接收缓存
// }
memset(rx_buffer,0,rx_len);
}
HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//重新打开DMA接收
}

stm32f1xx_it.c中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "usart.h"

void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint32_t tmp_flag = 0;
uint32_t temp;
tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
if((tmp_flag != RESET))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
//temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
//temp = huart1.Instance->DR; //读取数据寄存器中的数据
//这两句和上面那句等效
HAL_UART_DMAStop(&huart1); //
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
//temp = hdma_usart1_rx.Instance->NDTR;//读取NDTR寄存器 获取DMA中未传输的数据个数,
//这句和上面那句等效
rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
recv_end_flag = 1; // 接受完成标志位置1
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */

/* USER CODE END USART1_IRQn 1 */
}

发送数据-测试成功

加入PWM呼吸灯代码


测试成功

加入全部代码

PWM

实现LED呼吸灯效果

while 函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HAL_Delay(20);
if (dir)
ledrpwmval++;
else
ledrpwmval--;

if (ledrpwmval > 100 - 1)
dir = 0;
if (ledrpwmval == 0)
dir = 1;

__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, ledrpwmval);//设置占空比

user_main_printf("ledrpwmval:%d", ledrpwmval); //串口打印

PWM控制电机转速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
KeyNum = key_scan(1);//通过按键扫描函数,获取按键值
if (KeyNum == 1)
{
HAL_Delay(10);
key_t_speed += 20;
if (key_t_speed > 100)
{
key_t_speed = -100;
}
}

LCD_ShowString(0, 150, 240, 24, 24, "Speed:");
if (key_t_speed < 0)
{
speed_show = 0 - key_t_speed;
LCD_ShowString(90, 150, 240, 24, 24, "-");
LCD_ShowNum(100, 150, speed_show, 3, 24);
}
else
{
speed_show = key_t_speed;
LCD_ShowString(90, 150, 240, 24, 24, " ");
LCD_ShowNum(100, 150, speed_show, 3, 24);
}
Motor_SetSpeed(key_t_speed);

ADC双通道测电压

1
2
3
4
5
6
7
8
9
10
11
for (i = 0, ad1 = 0, ad2 = 0; i < 100;)
{
ad1 += ADC_Value[i++];
ad2 += ADC_Value[i++];
}
ad1 /= 50;
ad2 /= 50;
user_main_info("ADC测试");
// ad1*3.3f/4096,先乘1000,将整数部分和小数部分分开,
ADC_1v = (int)ad1 * 3.3f / 4096;
ADC_1v = ad1 * 3.3f / 4096 * 1000 - ADC_1v * 1000;

遇到的问题

LCD和OLED同时开启时,初始化失败


解决方案:
因为我一开始使用的I2C1,换到I2C2问题解决。