单片机综合训练

该系统使用STM32F103c8t6微控制器,通过按键控制舵机旋转,上位机通过USART和DMA通信设定角度。每5秒单片机向上传送舵机信息,光敏传感器监控环境并控制LED报警。OLED屏幕显示舵机角度、ADC转换值及指令状态。程序包括舵机驱动、按键处理、串口通信和定时器等功能模块。
摘要由CSDN通过智能技术生成

单片机综合训练

功能设计

  1. 按键控制舵机旋转(按下按键使舵机角度增加30°,超过180°则回到0°)
  2. 上位机控制舵机旋转角度(0-180°)
  3. 每五秒单片机传送一次信息给上位机
  4. 光敏传感器 控制LED报警
  5. OLED屏幕显示舵机角度,ADC转换数值,指令等信息

程序设计

所需外设

  1. 硬件: STM32F103c8t6、面包板、舵机、OLED屏幕、光敏传感器等
  2. 上位机与单片机的通信用USART1和DMA1(PA9 Tx, PA10 Rx)
  3. TIM1用来计时(5s 发送一次舵机的信息)
  4. TIM2_CH1 用来输出PWM信号控制舵机旋转角度 PA0
  5. B6、B7、B8、B9接OLED屏幕
  6. PA1作为按键输入(EXTI触发中断)
  7. ADC1 通道7(PA7) 转换光敏传感器获取的信号

流程图

在这里插入图片描述

效果展示 (视频)

【展示视频】

主要代码

main.c

#include "stm32f10x.h" 
#include "delay.h"
#include "OLED.h"
#include "key.h"
#include "Server.h"
#include "Timer.h"
#include "MyDMA.h"
#include "Serial.h"
#include "AD.h"
#include "LED.h"
/*
 *  单片机课设
 *  2023年4月18日16:06:37
 *  需求: 1. 上位机控制舵机旋转的角度(0-180°)
 *        2. 每5秒返回一次信息(传给上位机和OLED屏幕)
 *        3. 按下按键使舵机角度增加30°(若超过180°则回到0°)
 *        4. 光敏传感器 控制LED报警
 *		  5. OLED屏幕显示舵机角度、ADC、指令等信息
 *  程序设计:
 *        1. 硬件: STM32F103c8t6、面包板、舵机、OLED屏幕、光敏传感器
 *        2. 上位机与单片机的通信用USART1和DMA1(PA9 Tx, PA10 Rx)
 *        3. TIM1用来计时(5s 发送一次舵机的信息)
 *        4. TIM2_CH1 用来输出PWM信号控制舵机旋转角度  PA0
 * 		  5. B6、B7、B8、B9接OLED屏幕
 *        6. PA1作为按键输入(EXTI触发中断) 
 *        7. ADC1 通道7(PA7) 转换光敏传感器获取的信号
 */
int main(void)
{
	OLED_Init(); // 初始化OLED屏幕
	Key_Init(); // 初始化按键
	Server_Init(); // 舵机驱动初始化
	OLED_ShowString(1, 1, "Angle:");
	OLED_ShowString(2, 1, "AD_Value:");
	OLED_ShowString(3, 1, "Voltage:0.00V");
	Timer_Init(); // 初始化定时器
	USART_Tx_MyDMA_Init();  // USART发送DMA初始化
	USART_Rx_MyDMA_Init();	// USART接收DMA初始化
	Serial_Init(); // 初始化串口 
	AD_Init();
	LED_Init();
	while(1)
	{
		OLED_ShowNum(1, 7, Serve_Angle, 3);
		OLED_ShowNum(2, 10, AD_Value, 4);
		AD_Voltage = (float)AD_Value*3.3/4096;  // 获取电压值
		OLED_ShowNum(3, 9, AD_Voltage, 1);
		OLED_ShowNum(3, 11, (uint16_t)(AD_Voltage * 100)% 100, 2);
		LED_Off();  // LED保持关闭
	}
}

server.c (舵机驱动程序)

#include "stm32f10x.h"
float Serve_Angle;
// TIM2_CH1 用来输出PWM信号控制舵机旋转角度  PA0
// 频率50Hz
void Server_Init(void)
{
	// RCC使能时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	// 配置GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	// 选择内部时钟作为TIM2的时钟
	TIM_InternalClockConfig(TIM2);
	
	// 配置时基单元
	TIM_TimeBaseInitTypeDef TimeBase_InitStructure;
	TimeBase_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TimeBase_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TimeBase_InitStructure.TIM_Period=20000-1;   // ARR
	TimeBase_InitStructure.TIM_Prescaler=72-1;   // 预分频
	TimeBase_InitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2, &TimeBase_InitStructure);
	
	// 配置OC单元(输出比较单元 OutPut Compare)  OC1
	TIM_OCInitTypeDef OC_InitStructure;
	TIM_OCStructInit(&OC_InitStructure);
	OC_InitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	OC_InitStructure.TIM_Pulse=0;  // CCR寄存器
	OC_InitStructure.TIM_OCPolarity=TIM_OCPolarity_High; // 设置有效电平
	OC_InitStructure.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OC1Init(TIM2, &OC_InitStructure);
	
	// 开启TIM
	TIM_Cmd(TIM2, ENABLE);
}

/*
 *  设置OC1 CCR寄存器的值
 */
void Server_Set_Compare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);  
}


/*
	设置舵机角度 
	因为舵机需要20ms的波长来驱动 可知频率为50Hz 若ARR设置为 20000-1 则PSC设置为72-1
	又0.5ms~2.5ms对应舵机角度的0~180°
	0.5ms 对应ARR为500  2.5ms 对应ARR为2500
	对应角度的CCR应该设置为(Angle / 180 * 2000 + 500)
*/
void Server_SetAngle(float Angle)
{
	Server_Set_Compare1(Angle / 180 * 2000 + 500);
}

key.c (按键)

#include "stm32f10x.h"
#include "delay.h"
#include "Server.h"
// PA1作为按键输入
// 2023年4月18日16:32:06


/*
 *  按键初始化函数  下降沿触发 配置为上拉输入
 */
void Key_Init(void)
{
	// RCC 使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	// 配置GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 配置AFIO
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);  // PA1
	
	// 配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line1;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;  // 下降沿触发
	EXTI_Init(&EXTI_InitStructure);
	
	// 配置NVIC分组 只需配置一次
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	// 配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;  // 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;  // 响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
}

/*
 *  EXTI 中断函数
 */
void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1))
	{
		// 消抖
		Delay_ms(40);
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)==0)
		{	
			Serve_Angle += 30;
		}
		while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)==0);
		if(Serve_Angle>180)
		{
			Serve_Angle = 0;
		}
		Server_SetAngle((float)Serve_Angle);
		// 清除标志位
		EXTI_ClearITPendingBit(EXTI_Line1);
	}

}

LED.c (LED驱动程序)

#include "stm32f10x.h"
#include "delay.h"

/*
 *	初始化LED  PB10  低电平驱动
 */
void LED_Init(void)
{
	// RCC使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	// 配置GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10);
}

/*
 *  LED灭
 */
void LED_Off(void)
{
	GPIO_SetBits(GPIOB, GPIO_Pin_10);
}

/*
 *  LED亮
 */
void LED_On(void)
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_10);

}

Serial.c & MyDMA.c (串口和DMA)

#include "stm32f10x.h"
#include "Server.h"
#include <stdio.h>
#include "OLED.h"
// 上位机与单片机的通信用USART1和DMA1(PA9 Tx, PA10 Rx)
uint8_t Decode_Flag;
/*
 * 控制的指令
 */
const char Command1[9]="SET_ANGLE";

/*
 *  定义的缓存区 
 */
char TxBuffer[]="Angel:000\r\n";  // 舵机的角度信息 6 7 8 为数据位
char RxBuffer[15];  // 接收的指令

/*
 *  串口初始化函数
 */
void Serial_Init(void)
{
	// RCC使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	// 配置GPIO
	// Tx
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	// Rx
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 配置USART
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate=9600;
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStructure.USART_Parity=USART_Parity_No;  // 无奇偶校验
	USART_InitStructure.USART_StopBits=USART_StopBits_1; // 一位停止位
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;  // 传输字长
	USART_Init(USART1, &USART_InitStructure);
	
	// 打开USART中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  // 打开接收寄存器非空中断
	// 开启DMA请求
	USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);
	// 配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;  // 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;  // 响应优先级
	NVIC_Init(&NVIC_InitStructure);
	// 启动USART
	USART_Cmd(USART1, ENABLE);
	printf("Use [SET_ANGLE xxx] to set the angle of servo!\r\n");
	printf("Other functions are under development!\r\n");
}

/*
 *  重写fputc
 */
int fputc(int ch, FILE * f)
{
	USART_SendData(USART1, (uint8_t)ch);
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
	return ch;
}
	
/*
	获取舵机信息
*/
void Serial_Get_ServerInfo(void)
{
	uint8_t Angle = (uint8_t)Serve_Angle;
	if(Angle >= 100)
	{
		TxBuffer[8] = Angle % 10 + '0';
		Angle = Angle / 10;
		TxBuffer[7] = Angle % 10 + '0';
		TxBuffer[6] = Angle / 10 + '0';
	}
	else if(Serve_Angle >= 10)
	{
		TxBuffer[8] = Angle % 10 + '0';
		TxBuffer[7] = Angle / 10 + '0';
		TxBuffer[6] = '0';
	}
	else if(Serve_Angle >= 0)
	{
		TxBuffer[8] = Angle + '0';
		TxBuffer[7] = '0';
		TxBuffer[6] = '0';
	}
}

/*
 *  解码从上位机获取的信息
 */
uint8_t Serial_DecodeInfo(char * String)
{
	uint8_t i;
	for(i=0; i<9; ++i)
	{
		if(String[i] != Command1[i])
		{
			return 0;
		}
	}
	Serve_Angle = (String[10]-'0')*100 + (String[11]-'0')*10 + (String[12]-'0');
	if(Serve_Angle > 180) Serve_Angle = 0;
	Server_SetAngle((float)Serve_Angle);
	OLED_ShowString(4, 1, "                ");
	OLED_ShowString(4, 1, RxBuffer);
	return 1;
}

/*
	根据反馈解码状态打印信息
*/
void Decode_Info_Print(void)
{
	if(Decode_Flag == 0)
	{
		// 解码失败, 请用正确的协议进行通讯
		printf("Decoded failed!\r\nPlease ensure the communication protocol is correct!\r\n");
	}
	else
	{
		// 解码成功!LED1状态转换成功
		printf("Decoded successful!\r\n");
	}
	Decode_Flag = 0;
}

/*
 *  USART中断函数
 */
void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1, USART_IT_RXNE))
	{
		// 清除中断标志
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
#include "stm32f10x.h"
#include "Serial.h"
#include "AD.h"
#define TXBUFFERSIZE 11
#define RXBUFFERSIZE 15
/*
 * USART串口利用DMA发送数据 DMA初始化函数
 */
void USART_Tx_MyDMA_Init(void)
{
	// 打开DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	// 初始化DMA 利用DMA发送串口数据
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize=TXBUFFERSIZE;  // 缓存区大小
	DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)TxBuffer;  // 存储器地址(待发送数据地址)
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;  // 自增
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&USART1->DR;  // 外设地址(USART的发送数据寄存器)
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;  // 外设区数据大小
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;  // 不自增
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;  // 存储区到外设
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;  // 硬件触发
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;  // 非循环模式
	DMA_InitStructure.DMA_Priority=DMA_Priority_High;  // 设置优先级
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);
	DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);  // 开启传输完成中断
	DMA_Cmd(DMA1_Channel4, DISABLE);   // 先关闭DMA对应通道
	
	// 配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStructure);
}

/*
 *  USART串口利用DMA接收数据 DMA初始化函数
 */
void USART_Rx_MyDMA_Init(void)
{
	// 打开DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	// 初始化DMA 利用DMA发送串口数据
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize=RXBUFFERSIZE;  // 缓存区大小
	DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)RxBuffer;  // 存储器地址
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;  // 自增
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&USART1->DR;  // 外设地址(USART的接受寄存器)
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; 
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;  // 非自增
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;  // 外设到存储区
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;  // 硬件触发
	DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;  // 循环模式
	DMA_InitStructure.DMA_Priority=DMA_Priority_VeryHigh;  // 设置优先级
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);
	DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);  // 开启传输完成中断
	DMA_Cmd(DMA1_Channel5, ENABLE);   // 打开DMA对应通道
	
	// 配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel5_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);
}

/*
	DMA将ADC转换的数据搬运到AD_Value  DMA初始化函数
*/
void AD_MyDMA_Init(void)
{
	// RCC使能时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	// 配置DMA
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // ADC的数据寄存器
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_Value;  // 保存的地址
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  // 这里要自增
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  // 转运方向
	DMA_InitStructure.DMA_BufferSize = 1;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  // 自动重装
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;	 // 硬件触发
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	// 开启DMA
	DMA_Cmd(DMA1_Channel1, ENABLE);  // 通道1
}

/*
 *  将缓存区的数据转移到数据发送寄存器中
 */
void MyDMA_Transfer_Tx(void)
{
	// 关闭DMA通道
	DMA_Cmd(DMA1_Channel4, DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel4, TXBUFFERSIZE);
	DMA_Cmd(DMA1_Channel4, ENABLE);  // 开始转运
	while(DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET);   // 等待转运完成
	DMA_ClearFlag(DMA1_FLAG_TC4);
}

/*
	DMA通道1中断函数 数据搬运到发送寄存器完毕
*/
void DMA1_Channel1_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC1)==SET)
	{
		// 清除中断标志
		DMA_ClearITPendingBit(DMA1_IT_TC1);
	}

}

/*
	DMA通道4中断函数 数据搬运到发送寄存器完毕
*/
void DMA1_Channel4_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC4)==SET)
	{
		// 清除中断标志
		DMA_ClearITPendingBit(DMA1_IT_TC4);
	}

}

/*
	DMA通道5中断函数 接收寄存器中的数据搬运完毕
*/
void DMA1_Channel5_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC5)==SET)
	{
		// 解码
		Decode_Flag = Serial_DecodeInfo((char*)RxBuffer);
		// 打印解码信息
		Decode_Info_Print();  
		// 清除中断标志
		DMA_ClearITPendingBit(DMA1_IT_TC5);
	}
}

Timer.c (定时器)

#include "stm32f10x.h"
#include "Serial.h"
#include "MyDMA.h"
// TIM1用来计时

void Timer_Init(void)
{
	// RCC 使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
	
	// 配置TimerBase时钟为内部时钟
	TIM_InternalClockConfig(TIM1);
	
	// 配置TimerBase
	TIM_TimeBaseInitTypeDef TimerBaseInitStructure;
	TimerBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TimerBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TimerBaseInitStructure.TIM_Period=50000-1;
	TimerBaseInitStructure.TIM_Prescaler=7200-1;
	TimerBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM1, &TimerBaseInitStructure);
	TIM_ClearFlag(TIM1, TIM_FLAG_Update);
	
	// 使能更新中断
	TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
	
	// 配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM1_UP_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;  // 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;  // 响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
	// 开启TIM
	TIM_Cmd(TIM1, ENABLE);
}

/*
 *  TIM1中断函数
 */
void TIM1_UP_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM1, TIM_IT_Update))
	{
		// 获取舵机信息
		Serial_Get_ServerInfo();
		// DMA传送数据
		MyDMA_Transfer_Tx();
		// 清除标志位
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
	}
	
}

AD.c (ADC转换器)

#include "stm32f10x.h"
#include "MyDMA.h"
#include "LED.h"
uint16_t AD_Value;
float AD_Voltage;
/*
 *  初始化ADC
 */
void AD_Init(void)
{
	// 初始化DMA通道1运输ADC1的数据
	AD_MyDMA_Init();
	// RCC使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // 12MHz mm
	// 配置GPIO口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	// 选择规则通道
	ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_55Cycles5);
	
	// 配置ADC转换器
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  // 单次转换或者连续转换
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  // 数据对齐模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // ADC模式, 单独还是交叉
	ADC_InitStructure.ADC_NbrOfChannel = 1;  // 扫描的通道数
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;  // 扫描模式或者非扫描模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  // 触发控制
	ADC_Init(ADC1, &ADC_InitStructure);
	
	// 开启DMA转运
	ADC_DMACmd(ADC1, ENABLE);
	
	// 开启模拟看门狗
	ADC_AnalogWatchdogThresholdsConfig(ADC1, 0xFFF, 0x5DC);  // 设置看门狗阈值
	ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_7); // 对通道7设置看门狗
	ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable);  // 使能单通道模拟看门狗
	ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);  // 开启模拟看门狗中断
	
	// 配置NVCI
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=ADC1_2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStructure);
	// 开启ADC功能
	ADC_Cmd(ADC1, ENABLE);
	
	// ADC校准
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);  // 已初始化为零
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET); 
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

/*
 *  ADC中断函数
 */
void ADC1_2_IRQHandler(void)
{
	if(ADC_GetITStatus(ADC1, ADC_IT_AWD)==SET)
	{
		// 开启警报灯
		LED_On();
		// 清除中断标志
		ADC_ClearITPendingBit(ADC1, ADC_IT_AWD);
	}

}

主要问题分析

  1. USART+DMA搬运的数据都是定长的, 如果发送不定长的数据, 则会出现数据传输混乱的现象, 造成后续的解码失败。后续若要添加不同的指令功能也不方便。

  2. 最初设计是温度过高发出警报, 由于环境温度难以控制, 采用光敏传感器替代。

  3. 关于串口通信这一方面, 感觉还能有更好的解码编码和传输的方法。未考虑到若数据传输错误该执行哪些操作。

    也未实验过大量数据传输时会发生什么,感觉通信这一块的代码还不是很稳定。

参考资料

STM32F10xxx参考手册(中文)

STM32F103xx固件函数库用户手册

STM32F103C8T6引脚定义

(STM32f030c8t6单片机ADC模拟看门狗使用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值