单片机综合训练
功能设计
- 按键控制舵机旋转(按下按键使舵机角度增加30°,超过180°则回到0°)
- 上位机控制舵机旋转角度(0-180°)
- 每五秒单片机传送一次信息给上位机
- 光敏传感器 控制LED报警
- OLED屏幕显示舵机角度,ADC转换数值,指令等信息
程序设计
所需外设
- 硬件: STM32F103c8t6、面包板、舵机、OLED屏幕、光敏传感器等
- 上位机与单片机的通信用USART1和DMA1(PA9 Tx, PA10 Rx)
- TIM1用来计时(5s 发送一次舵机的信息)
- TIM2_CH1 用来输出PWM信号控制舵机旋转角度 PA0
- B6、B7、B8、B9接OLED屏幕
- PA1作为按键输入(EXTI触发中断)
- 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);
}
}
主要问题分析
-
USART+DMA搬运的数据都是定长的, 如果发送不定长的数据, 则会出现数据传输混乱的现象, 造成后续的解码失败。后续若要添加不同的指令功能也不方便。
-
最初设计是温度过高发出警报, 由于环境温度难以控制, 采用光敏传感器替代。
-
关于串口通信这一方面, 感觉还能有更好的解码编码和传输的方法。未考虑到若数据传输错误该执行哪些操作。
也未实验过大量数据传输时会发生什么,感觉通信这一块的代码还不是很稳定。
参考资料
STM32F10xxx参考手册(中文)
STM32F103xx固件函数库用户手册
STM32F103C8T6引脚定义