[TOC]

任务(1):ADC 采样并通过串⼝发送⾄上位机

ADC简介

image-20241102110242914

ADC原理(逐次比较型)

image-20241102110346796

工作流程

  1. 初始化

    选择输入信号并设置参考电压。

  2. 采样

    ADC 读取输入模拟信号,并在采样保持电路中保持该值。

  3. 逐次比较

    ADC 开始进行逐次比较,将输入信号与内部的数字化值进行比较。这个过程通过一个逐次逼近寄存器实现。

    设定一个初始的数字值(通常是中间值),然后将其转换为模拟信号(DAC 输出),并与输入信号进行比较。

  4. 比较结果

    如果 DAC 输出的模拟信号大于输入信号,则该位为 0;如果小于,则该位为 1。根据比较结果,SAR 更新其当前的数字值。

    逐位比较过程从最高位到最低位进行,逐次确定每一位的值。

  5. 重复过程

    重复以上步骤,直到所有位都确定为止。一般来说,逐次比较型 ADC 的转换过程需要 N 次比较,其中 N 是 ADC 的分辨率(位数)。

  6. 输出结果

    一旦所有位都确定,转换结果就存储在输出寄存器中,可以通过数字接口读取。

image-20241102110819185

ADC基本结构

image-20241102111034836

ADC四种工作模式

  • 单次转换,扫描:配置多个通道,每次扫描可以扫描多通道,但每个大过程都要用函数触发
  • 单次转换,非扫描:配置单个同通道,每次扫描都要用一个函数触发一次
  • 连续转换,扫描:配置多个通道,进行连续扫描,只需触发一次
  • 连续转换,非扫描:配置一个通道,只扫描他,只需触发一次

转换时间

image-20241102111751744

程序设计

(技术点介绍)首先是配置adc及其中断:

时钟配置
1
2
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

这两行代码开启了 ADC3 和 GPIOC 的时钟。只有在时钟开启后,ADC 才能工作

ADC 时钟分频
1
RCC_ADCCLKConfig(RCC_PCLK2_Div6);

此行设置 ADC 的时钟分频,2和4不能选,只能从6开始选

GPIO 配置

将 ADC 的输入引脚配置为模拟输入模式:

1
2
3
4
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_Init(GPIOC, &GPIO_InitStructure);

这里配置了 GPIOC 的 6 到 9 引脚为模拟输入,以便 ADC 可以读取这些引脚上的电压信号。

ADC 配置

使用 ADC_InitTypeDef 结构体配置 ADC 参数:

1
2
3
4
5
6
7
8
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC3, &ADC_InitStructure);
  • 独立模式:表示此 ADC 不与其他 ADC 共享工作。
  • 数据对齐:选择数据右对齐,可以更容易地处理和解析结果。
  • 外部触发:这里设置为无外部触发,表示使用软件触发进行转换。
  • 连续转换:设置为禁用,这样 ADC 只在每次软件触发时进行一次转换。
  • 扫描模式:设置为禁用,表示只配置单个通道进行转换。
使能 ADC 和校准
1
2
3
4
5
ADC_Cmd(ADC3, ENABLE);
ADC_ResetCalibration(ADC3);
while (ADC_GetResetCalibrationStatus(ADC3) == SET);
ADC_StartCalibration(ADC3);
while (ADC_GetCalibrationStatus(ADC3) == SET);

这些代码段使能 ADC,并进行校准以确保测量的准确性。校准过程包括重置校准状态和开始校准。

中断配置
1
2
3
4
5
6
7
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = ADC3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
ADC_ITConfig(ADC3, ADC_IT_EOC, ENABLE);
  • EOC 中断:使能 EOC 中断,这样 ADC 转换完成时会触发中断
数据读取和处理
启动 ADC 转换

AD_GetValue 函数中,选择通道并启动转换:

1
2
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC3, ENABLE);

这段代码根据输入参数配置要读取的 ADC 通道,并通过软件触发开始转换。ADC_SampleTime_55Cycles5 表示采样时间为 55.5 个 ADC 时钟周期,这是一个影响转换速度和精度的参数。

中断服务例程

中断服务例程负责处理 ADC 转换完成的事件:

1
2
3
4
5
6
7
8
9
void ADC3_IRQHandler(void)
{
if (ADC_GetITStatus(ADC3, ADC_IT_EOC) != RESET)
{
adc_value = ADC_GetConversionValue(ADC3);
adc_ready = 1;
ADC_ClearITPendingBit(ADC3, ADC_IT_EOC);
}
}
  • 检查 EOC 标志:如果 EOC 中断被触发,则读取 ADC 转换结果,并将其存储在 adc_value
  • 清除中断标志:清除中断标志以便下次触发。

配置过程中,有几个点:

  • 因为是单次转换,非连续模式,所以想让它持续工作,每次调用函数时都要手动

    ADC_SoftwareStartConvCmd(ADC3, ENABLE)一次

  • 因为程序没有用到dma,所以只能使用单通道,否则数据村不过来,因此就用四个通道周期性扫描的方式来完成

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "stm32f10x.h"        // Device header
#include "Timer.h"
uint32_t mytime = 0;
uint32_t last_time = 0;

uint16_t adc_value = 0; // 用于保存 ADC 读取的值

void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE); // 开启 ADC3 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 开启 GPIOC 的时钟

RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 选择时钟 6 分频,ADCCLK = 72MHz / 6 = 12MHz

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
GPIO_Init(GPIOC, &GPIO_InitStructure); // 模拟输入

// ADC 初始化
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 模式,选择独立模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 外部触发,使用软件触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 连续转换,失能
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 扫描模式,失能
ADC_InitStructure.ADC_NbrOfChannel = 1; // 通道数为 1
ADC_Init(ADC3, &ADC_InitStructure);

ADC_Cmd(ADC3, ENABLE); // 使能 ADC3

// ADC 校准
ADC_ResetCalibration(ADC3); // 固定流程
while (ADC_GetResetCalibrationStatus(ADC3) == SET);
ADC_StartCalibration(ADC3);
while (ADC_GetCalibrationStatus(ADC3) == SET);

// 配置 ADC 中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = ADC3_IRQn;// 确保是 ADC3 的中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

ADC_ITConfig(ADC3, ADC_IT_EOC, ENABLE); // 使能 ADC 的 EOC 中断
}

uint16_t AD_GetValue(int recentchanel)
{
if (recentchanel == 4)
{
ADC_RegularChannelConfig(ADC3, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
}
else if (recentchanel == 5)
{
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 1, ADC_SampleTime_55Cycles5);
}
else if (recentchanel == 6)
{
ADC_RegularChannelConfig(ADC3, ADC_Channel_6, 1, ADC_SampleTime_55Cycles5);
}
else if (recentchanel == 7)
{
ADC_RegularChannelConfig(ADC3, ADC_Channel_7, 1, ADC_SampleTime_55Cycles5);
}

ADC_SoftwareStartConvCmd(ADC3, ENABLE); // 软件触发 ADC 转换一次
return adc_value; // 返回最新的 ADC 值
}

void ADC3_IRQHandler(void) // ADC3 中断处理函数
{
if (ADC_GetITStatus(ADC3, ADC_IT_EOC) != RESET) // 检查是否是 EOC 中断
{
adc_value = ADC_GetConversionValue(ADC3); // 读取 ADC 值
ADC_ClearITPendingBit(ADC3, ADC_IT_EOC); // 清除中断标志位
}
}


uint32_t get_time_interval(void) {
uint32_t current_time = mytime;
uint32_t interval = current_time - last_time; // 计算时间间隔
last_time = current_time; // 更新上次时间
return interval/100;//微秒化成毫秒
}


(技术点介绍,定时器中断)下面是timer.h

因为要获取时间间隔,所以配置tim定时器

思路如下:

  • 定义一个全局变量 mytime
  • 定义一个变量 last_time
  • timer配置分频72-1,自动重装值1
  • 配置中断,每次中断,mytime都加一,实现微秒级别的计数
  • 当调用adc相关函数的时候,计算mytime与last_time的差值
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
32
33
#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能时钟

TIM_InternalClockConfig(TIM2);

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //ָ设置时钟分频(1分频)
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStructure.TIM_Period=10-1;//周期是10微秒
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; //72mzh / 7200 = 10k ,72mhz / 7200 = 10k
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数(高级计时器有,现在不用)
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清除TIM2的更新中断标志位,确保定时器开始时没有残留的中断标志

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启更新中断到nvic通路

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel= TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2,ENABLE);

}


主函数:

定义一个四个变量的数组,用于标志各个红外对管的状态,只有满足状态改变的判定条件时,再改变状态,防止串口那里一直乱跳

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "stm32f10x.h"      // Device header
#include "LED.h"
#include "DELAY.h"
#include "Serial.h"
#include "Oled.h"
#include "LED.h"
#include "string.h"
#include "AD.h"
#include "Timer.h"
uint8_t RxData;
int ifblack[4];
extern uint32_t mytime;
void BlkOrWte(int a,int recentchanel);
int recentchanel = 4;
int main(void)
{
OLED_Init();
AD_Init();
Serial_Init();
Timer_Init();
while(1)
{
OLED_ShowNum(1,6,mytime,9);
if(recentchanel == 4)
{
int a = AD_GetValue(recentchanel);
BlkOrWte(a,recentchanel);
recentchanel = 5;
OLED_ShowNum(1,1,a,4);
}
else if(recentchanel == 5)
{
int a = AD_GetValue(recentchanel);
BlkOrWte(a,recentchanel);
recentchanel = 6;
OLED_ShowNum(2,1,a,4);
}
else if(recentchanel == 6)
{
int a = AD_GetValue(recentchanel);
BlkOrWte(a,recentchanel);
recentchanel = 7;
OLED_ShowNum(3,1,a,4);
}
else if(recentchanel == 7)
{
int a = AD_GetValue(recentchanel);
BlkOrWte(a,recentchanel);
recentchanel = 4;
OLED_ShowNum(4,1,a,4);
}
}
}
void BlkOrWte(int a,int recentchanel)
{
if(a>2000)
{
if(ifblack[recentchanel-4]==0)
{
Serial_SendNumber(recentchanel,1);
Serial_SendString(":black\r\n");
Serial_SendNumber(get_time_interval(),1);
Serial_SendString("\r\n");
}
ifblack[recentchanel-4] = 1;
}

if(a<500)
{
if(ifblack[recentchanel-4] == 1)
{
Serial_SendNumber(recentchanel,1);
Serial_SendString("white\r\n");
Serial_SendNumber(get_time_interval(),1);
Serial_SendString("\r\n");
}
ifblack[recentchanel-4] = 0;
}
}

void TIM2_IRQHandler(void) //更新中断函数
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
mytime++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除更新中断标志位
}
}

视频现象:

任务2 ——DMA转运数据

dma简介

image-20241102130840765

技术点介绍

DMA 配置和使⽤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//DMA初始化
DMA_InitTypeDef DMA_InitStructure; //定义结构体变量
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR; //外设基地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,半字,对应16为的ADC数据寄存器
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址自增,选择失能,始终以ADC数据寄存器为源
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器基地址,AD_Value
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度,半字,与源数据宽度对应
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增使能,每次转运后,数组位置下移
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向,选择由外设到存储器,ADC数据寄存器转到数组
DMA_InitStructure.DMA_BufferSize = 4; //转运次数,与ADC通道数一致
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式,与ADC的连续转换一致
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器失能
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级,中等
DMA_Init(DMA2_Channel5, &DMA_InitStructure); //将结构体变量交给DMA_Init,配置DMA2的通道5
DMA_InitTypeDef 结构体成员
  1. DMA_PeripheralBaseAddr

    设置外设的基地址,DMA 传输时的源地址。

    通常是外设寄存器的地址,比如 ADC 的数据寄存器 &ADC3->DR

  2. DMA_PeripheralDataSize

    设置外设数据的宽度。

    DMA_PeripheralDataSize_Byte:8位数据宽度

    DMA_PeripheralDataSize_HalfWord:16位数据宽度(通常用于 ADC)

    DMA_PeripheralDataSize_Word:32位数据宽度

  3. DMA_PeripheralInc

    设置外设地址自增。

    DMA_PeripheralInc_Disable:外设地址不自增(常用于 ADC)。

    DMA_PeripheralInc_Enable:外设地址自增

  4. DMA_MemoryBaseAddr

    设置存储器的基地址,DMA 将数据写入的目的地

    通常是指向一个数组或内存区域的指针,例如 AD_Value

  5. DMA_MemoryDataSize

    设置存储器数据的宽度。

    DMA_Memorialized_Byte:8位数据宽度。

    DMA_Memorialized_HalfWord:16位数据宽度。

    DMA_Memorialized_Word:32位数据宽度。

  6. DMA_MemoryInc

    设置存储器地址自增。

    DMA_MemoryInc_Disable:存储器地址不自增(所有数据写入同一位置)。

    DMA_MemoryInc_Enable:存储器地址自增(每次传输后,指向下一个存储单元)。

  7. DMA_DIR

    设置数据传输的方向。

    DMA_DIR_PeripheralSRC:从外设到存储器(如 ADC 到内存)。

    DMA_DIR_MemorySRC:从存储器到外设(如内存到 DAC)。

  8. DMA_BufferSize

    设置传输的数据大小(传输次数)。

    指定传输的样本数量。例如,若你要从 ADC 获取 4 个样本,则设置为 4。

  9. DMA_Mode

    设置 DMA 的工作模式。

    DMA_Mode_Normal:正常模式,传输一次后停止。

    DMA_Mode_Circular:循环模式,完成一次传输后自动重新开始,适合连续数据采集。

  10. DMA_M2M

    设置存储器到存储器传输的使能。

    DMA_M2M_Disable:禁用存储器到存储器的传输(通常用于外设到存储器传输)。

    DMA_M2M_Enable:启用存储器到存储器的传输(适用于特定应用)。

  11. DMA_Priority

    设置 DMA 传输的优先级。

    DMA_Priority_Low:低优先级。

    DMA_Priority_Medium:中优先级。

    DMA_Priority_High:高优先级。

    DMA_Priority_VeryHigh:最高优先级。

ADC 触发 DMA

  1. DMA 配置

    在 ADC 初始化中,可以配置 DMA 使能。当 ADC 完成一次转换时,它会自动向 DMA 发送一个信号,指示数据已经准备好进行传输

    1
    ADC_DMACmd(ADC3, ENABLE);
  2. 数据传输

    一旦 DMA 接收到来自 ADC 的触发信号,它会从 ADC 的数据寄存器中读取转换结果,并将其存储到指定的内存地址(例如某个数组中)

定时器配置

10微秒为周期,以便记录微妙级别的DMA过程(也不是很精准,DMA转运太快了)

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
32
#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能时钟

TIM_InternalClockConfig(TIM2);

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //ָ设置时钟分频(1分频)
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStructure.TIM_Period=10-1;//周期是10微秒
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1; //72mzh / 7200 = 10k ,72mhz / 7200 = 10k
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数(高级计时器有,现在不用)
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清除TIM2的更新中断标志位,确保定时器开始时没有残留的中断标志

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启更新中断到nvic通路

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel= TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2,ENABLE);

}

主函数源码

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "stm32f10x.h"      // Device header
#include "LED.h"
#include "DELAY.h"
#include "Serial.h"
#include "Oled.h"
#include "LED.h"
#include "string.h"
#include "AD.h"
#include "Timer.h"
int delta_time = 0;
int ifblack[4];
extern uint32_t mytime;
int dma_transfer_count =0;
void BlkOrWte(int a,int recentchanel);
int recentchanel = 4;
int main(void)
{
OLED_Init();
AD_Init();
Serial_Init();
Timer_Init();
while(1)
{
OLED_ShowNum(1,1,mytime,10);
Serial_SendNumber(AD_Value[0],4);
Serial_SendString(" ");
Serial_SendNumber(AD_Value[1],4);
Serial_SendString(" ");
Serial_SendNumber(AD_Value[2],4);
Serial_SendString(" ");
Serial_SendNumber(AD_Value[3],4);
Serial_SendString(" delta_time:");
Serial_SendNumber(delta_time,5);
Serial_SendString("\r\n");
}
}


void TIM2_IRQHandler(void) //更新中断函数
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //获取TIM3定时器的更新中断标志位
{
mytime++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除更新中断标志位
}
}

void DMA2_Channel4_5_IRQHandler(void) //配置一下dma2的中断函数
{
if (DMA_GetITStatus(DMA2_IT_TC5)) {
dma_transfer_count++; // 增加转运计数
if (dma_transfer_count >= 1) { // 每次转运计算时间间隔
delta_time = get_time_interval(); // 计算时间间隔
dma_transfer_count = 0; // 重置计数器
}
DMA_ClearITPendingBit(DMA2_IT_TC5); // 清除中断标志
}
}

视频现象:

vofa

JustFloat 协议的结构

  1. 数据帧:
    每个数据帧包含一个浮点数组,数组中的每个元素代表一个通道的数据。例如,有四个通道的数据要传输,数据帧中就要包含四个浮点数
  2. 小端格式:
    JustFloat 协议使用小端格式存储浮点数,最低有效字节存储在最低的内存地址中
  3. 帧尾:
    每个数据帧的末尾都有一个固定的帧尾标志,用于标识数据帧的结束: {0x00, 0x00, 0x80, 0x7f}

数据传输过程

  1. 数据准备:

    需要传输的数据首先被转换成浮点数,并按照小端格式存储在数组中。

  2. 数据发送:
    将浮点数组和帧尾标志一起发送到接收端。接收端通过识别帧尾标志来确定数据帧的结束,并解析浮点数组中的数据。

  3. 数据解析:

    接收端接收到数据帧后,解析浮点数组中的数据,并根据需要进行处理或显示。

应用场景

适合多通道数据采集,例如正在做的循迹小车有多个红外对管,每个红外对管代表一个通道的数据。

图形控件的使用

以最常用的示波器为例

image-20241103190053878

在左侧选择控件示波器并把它拖进去

  • 右键可以选择填充方式
  • 下面三个圈可以调整波形范围,包含点数等
  • auto可以让波形以最佳方式自动呈现

具体使用方法

配置一下这个能用这个协议的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "stm32f10x.h"                  // Device header
#include "string.h"
#include "serial.h"
void Vofa_JustFloat(float *data, uint8_t num)
{
uint8_t tempData[100];
uint8_t temp_end[4] = {0x00, 0x00, 0x80, 0x7f};
float temp_copy[num];

memcpy(&temp_copy, data, sizeof(float) * num);

memcpy(tempData, (uint8_t *)&temp_copy, sizeof(temp_copy));
memcpy(&tempData[num * 4], &temp_end[0], 4);

//串口发送函数
Serial_SendArray(tempData,(num + 1) * 4);
}

  • 这个函数中,前几行都是在进行数据处理,处理完成后得到tempdata,是一个uint8_t类型的数组
  • 得到数组之后,用江科大的串口发送函数直接发到送即可
  • 使用方法:输入(浮点数数组,数组长度)

配置ADC&&DMA

依然采用上面的配置,并将ADC数据接入拓展到12?个(咱只发了10个红外对管)

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "stm32f10x.h"        // Device header
#include "Timer.h"
uint32_t mytime = 0;
uint32_t last_time = 0;


uint16_t AD_Value_for_DMA2[5];
uint16_t AD_Value_for_DMA1[5];//用于存放AD转换结果的全局数组


void AD_Init(void)
{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE); //开启ADC3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE); //开启GPIOF的时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); //开启DMA2的时钟


RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz


GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 |GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOF, &GPIO_InitStructure);

//剩下的
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 |GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);



ADC_RegularChannelConfig(ADC3, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_6, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_7, 4, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_8, 5, ADC_SampleTime_55Cycles5);

ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 6, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 7, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 8, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 9, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 10, ADC_SampleTime_55Cycles5);


ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //这里好像还要改一个独立模式?
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_NbrOfChannel = 10;
ADC_Init(ADC3, &ADC_InitStructure);

//DMA初始化
DMA_InitTypeDef DMA_InitStructure; //定义结构体变量
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR; //外设基地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,半字,对应16为的ADC数据寄存器
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址自增,选择失能,始终以ADC数据寄存器为源
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value_for_DMA2; //存储器基地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度,半字,与源数据宽度对应
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增使能,每次转运后,数组位置下移
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向,选择由外设到存储器,ADC数据寄存器转到数组
DMA_InitStructure.DMA_BufferSize =5; //转运次数
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式,与ADC的连续转换一致
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器失能
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级,中等
DMA_Init(DMA2_Channel5, &DMA_InitStructure); //将结构体变量交给DMA_Init,配置DMA2的通道5


DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设基地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,半字,对应16为的ADC数据寄存器
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址自增,选择失能,始终以ADC数据寄存器为源
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value_for_DMA1; //存储器基地址
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度,半字,与源数据宽度对应
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增使能,每次转运后,数组位置下移
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向,选择由外设到存储器,ADC数据寄存器转到数组
DMA_InitStructure.DMA_BufferSize =5; //转运次数
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式,与ADC的连续转换一致
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器失能
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级,中等
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //将结构体变量交给DMA_Init,配置DMA1的通道1

DMA_Cmd(DMA1_Channel1, ENABLE);
DMA_Cmd(DMA2_Channel5, ENABLE);
ADC_DMACmd(ADC3, ENABLE); //ADC3触发DMA2的信号使能
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC3, ENABLE);

DMA_ITConfig(DMA2_Channel5, DMA_IT_TC, ENABLE); // 启用传输完成中断

// 设置 DMA 中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel4_5_IRQn; //45共享中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 先占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 从优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure);


DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); // 启用传输完成中断

// 设置 DMA 中断

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 先占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 从优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure);

//ADC校准
ADC_ResetCalibration(ADC3); //固定流程,内部有电路会自动执行校准
while (ADC_GetResetCalibrationStatus(ADC3) == SET);
ADC_StartCalibration(ADC3);
while (ADC_GetCalibrationStatus(ADC3) == SET);

ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准
while (ADC_GetResetCalibrationStatus(ADC3) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC3) == SET);

//ADC触发
ADC_SoftwareStartConvCmd(ADC3, ENABLE);
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作
}

main函数调用

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "stm32f10x.h"      // Device header
#include "LED.h"
#include "DELAY.h"
#include "Serial.h"
#include "Oled.h"
#include "LED.h"
#include "string.h"
#include "AD.h"
#include "Timer.h"
#include "vofa.h"
float mydata[10];
int main()
{
Serial_Init();
OLED_Init();
AD_Init();
while(1)
{


}
}

//以dma2为主,dma2触发以后,顺便把dma1和dam2的一块发出去,当然dma1的自己也存一遍
void DMA2_Channel4_5_IRQHandler(void)
{
if (DMA_GetITStatus(DMA2_IT_TC5)) {
mydata[0] = AD_Value_for_DMA2[0];
mydata[1] = AD_Value_for_DMA2[1];
mydata[2] = AD_Value_for_DMA2[2];
mydata[3] = AD_Value_for_DMA2[3];
mydata[4] = AD_Value_for_DMA2[4];
mydata[5] = AD_Value_for_DMA1[0];
mydata[6] = AD_Value_for_DMA1[1];
mydata[7] = AD_Value_for_DMA1[2];
mydata[8] = AD_Value_for_DMA1[3];
mydata[9] = AD_Value_for_DMA1[4];

Vofa_JustFloat(mydata,10);
DMA_ClearITPendingBit(DMA2_IT_TC5); // 清除中断标志
}
}
void DMA1_Channel1_IRQHandler(void)
{
if (DMA_GetITStatus(DMA1_IT_TC1)) {
mydata[5] = AD_Value_for_DMA1[0];
mydata[6] = AD_Value_for_DMA1[1];
mydata[7] = AD_Value_for_DMA1[2];
mydata[8] = AD_Value_for_DMA1[3];
mydata[9] = AD_Value_for_DMA1[4];

DMA_ClearITPendingBit(DMA1_IT_TC1); // 清除中断标志
}
}

视频现象:

任务四:进行简单的循迹

外设原理

红外对管:红外对管循迹的原理主要是利用了不同颜色物体对红外线的反射和吸收能力不同。其基本工作过程如下:

红外发射:红外对管由一个红外发射管和一个红外接收管组成。红外发射管通电后会持续向外发射特定波长的红外线。

光线反射:当红外光线照射到物体表面时,根据物体的颜色和材质特性,会对红外线产生不同的反射情况。白色物体对光线的反射能力较强,能够将大部分照射到其表面的红外线反射出去;而黑色物体对光线的吸收能力较强,反射的红外线则较少。

接收与转换:红外接收管是一种对红外线敏感的光敏元件。当接收管接收到反射回来的红外线时,其内部的物理特性会发生变化,从而产生电流或电压的变化。如果接收到的红外光强度较大,接收管产生的电流或电压就会相应较大;反之,如果接收到的红外光很弱或几乎没有,接收管产生的电流或电压就会非常小。

信号输出与判断:通常会将红外接收管的输出信号连接到一个比较电路或信号处理电路。该电路会将接收管输出的信号与一个预设的阈值进行比较。如果接收管输出的信号大于阈值,说明接收到的红外光较强,此时判断为红外对管处于白色区域;如果接收管输出的信号小于阈值,说明接收到的红外光较弱,此时判断为红外对管处于黑色轨迹上。

基于以上原理,将红外对管安装在移动的物体(如智能小车)上,并合理设置多个红外对管的位置,就可以通过检测不同位置的红外对管信号,判断物体相对于黑线轨迹的位置,从而实现循迹功能。例如,当小车的左侧红外对管检测到黑色轨迹,而右侧红外对管检测到白色区域时,说明小车向左偏离了轨迹,控制系统就可以控制小车向右转,以保持在黑色轨迹上行驶。

循迹模块:根据红外对管反射回的信息,来判断小车行走的状态

5个红外对管安装在小车的对应位置

将ADC值转换为黑线1或者没有线0,红外对管从左往右分别为IPT0,IPT1,IPT2,IPT3,IPT4

我们根据反射回的信息将小车运动分为几种情况

PID:比例-积分-微分控制器,是一种常用的反馈控制算法,广泛应用于工业控制、自动化等领域。

基本原理

  1. 比例控制(P)

比例控制是根据当前的误差值与一个比例系数相乘来计算控制量。误差值是设定值与实际值之间的差值。

例如,如果设定温度为 50℃,当前实际温度为 45℃,误差为 5℃。假设比例系数为 2,那么比例控制的输出为 5×2 = 10。

比例控制的作用是对误差进行快速响应,但不能消除稳态误差,即当系统达到稳定状态时,可能仍然存在一定的误差。

  1. 积分控制(I)

积分控制是对误差进行累积求和,并将累积的误差与一个积分系数相乘来计算控制量。

随着时间的推移,积分项会不断增大,直到误差为零。积分控制的作用是消除稳态误差,但积分作用过强可能会导致系统响应变慢或出现超调。

例如,在温度控制中,如果误差一直存在,积分项会不断累积,使得控制量逐渐增大,直到温度达到设定值。

  1. 微分控制(D)

微分控制是根据误差的变化率(即当前误差与上一时刻误差的差值)与一个微分系数相乘来计算控制量。

微分控制的作用是预测误差的变化趋势,提前给出控制信号,从而减小超调量和提高系统的稳定性。

例如,如果温度上升速度很快,微分控制会产生一个较大的负控制量,以抑制温度的快速上升。

工作过程

首先,测量系统的实际输出值。

然后,计算实际输出值与设定值之间的误差。

分别计算比例、积分和微分三个部分的控制量:

比例控制量 = 比例系数 × 误差

积分控制量 = 积分系数 × 误差的累积和

微分控制量 = 微分系数 × 误差的变化率

将三个部分的控制量相加,得到总的控制量。

根据控制量来调整系统的输入,使系统的输出逐渐接近设定值。

外设应用

我们根据反射回的信息将小车运动分为以下几种情况

  1. 直行

此时五个红外对管反射回的信息为00100

1
2
3
4
if(IPT[0]==0&&IPT[1]==0&&IPT[2]==1&&IPT[3]==0&&IPT[4]==0)
{
qianjin();
}
  1. 左转90度

此时五个红外对管反射回的信息为11000或者11100

开始左转

1
2
3
4
5
else if(IPT[0]==1&&IPT[1]==1&&IPT[3]==0&&IPT[4]==0)

{
turnleft(90);
}
  1. 右转90度

此时五个红外对管反射回的信息为00011或者00111

开始右转

1
2
3
4
5
	  else if(IPT[0]==0&&IPT[1]==0&&IPT[3]==1&&IPT[4]==1)

{
turnright(90);
}
  1. 向左偏一些

此时五个红外对管反射回的信息为01000

1
2
3
4
5
else if(IPT[0]==0&&IPT[1]==1&&IPT[2]==0&&IPT[3]==0&&IPT[4]==0)

{
turnleft(30);
}
  1. 向右偏一些

此时五个红外对管反射回的信息为00010

1
2
3
4
5
else if(IPT[0]==0&&IPT[1]==0&&IPT[2]==0&&IPT[3]==1&&IPT[4]==0)

{
turnright(30);
}
  1. 掉头

此时五个红外对管反射回的信息为00000

1
2
3
4
5
else if(IPT[0]==0&&IPT[1]==0&&IPT[2]==0&&IPT[3]==0&&IPT[4]==0)

{
turnright(180);
}
  1. 在遇见岔路时,选择右转
1
2
3
4
5
else if(IPT[0]==1&&IPT[1]==1&&IPT[2]==1&&IPT[3]==1&&IPT[4]==1)

{
turnright(90);
}

使用直流电机作为动力源,通过电机驱动电路来控制电机的转速和方向。

通过控制左右两个电机的转速和方向,可以使小车实现前进、后退、转弯等动作。例如,当左侧电机转速高于右侧电机转速时,小车会向右转;当右侧电机转速高于左侧电机转速时,小车会向左转。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void leftforward(void)
{
PDout(12,1);
PDout(13,0);
PDout(14,1);
PDout(15,0);
}

void rightforward(void)
{
PEout(9,1);
PEout(11,0);
PBout(10,1);
PBout(11,0);
}

void leftbackward(void)
{
PDout(12,0);
PDout(13,1);
PDout(14,0);
PDout(15,1);
}

void rightbackward(void)
{
PEout(9,0);
PEout(11,1);
PBout(10,0);
PBout(11,1);
}

void turnright(uint16_t angle)
{
int dt = (8*angle);
leftforward();
rightbackward();
Delay_ms(dt);

}

void turnleft(uint16_t angle)
{
int dt = (8*angle);
leftbackward();
rightforward();
Delay_ms(dt);

}

使用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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "stm32f10x.h"                  // Device header

void my_set_tim_compare(uint16_t brr)
{
TIM_SetCompare2(TIM4,brr);
TIM_SetCompare1(TIM4,brr);
TIM_SetCompare2(TIM2,brr);
TIM_SetCompare1(TIM2,brr);
TIM_SetCompare2(TIM1,brr);
TIM_SetCompare1(TIM1,brr);
}

void PDout(uint8_t pin, uint8_t value) {
if (value) {
GPIO_SetBits(GPIOD, 1 << pin); // Set the pin high
} else {
GPIO_ResetBits(GPIOD, 1 << pin); // Set the pin low
}
}

void PEout(uint8_t pin, uint8_t value) {
if (value) {
GPIO_SetBits(GPIOE, 1 << pin); // Set the pin high
} else {
GPIO_ResetBits(GPIOE, 1 << pin); // Set the pin low
}
}

void PBout(uint8_t pin, uint8_t value) {
if (value) {
GPIO_SetBits(GPIOB, 1 << pin); // Set the pin high
} else {
GPIO_ResetBits(GPIOB, 1 << pin); // Set the pin low
}
}


void TIM4_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;



RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);



GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//TIM4通道2

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//TIM4通道1

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_12);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_13);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_14);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_15);

TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM4, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);


TIM_Cmd(TIM4, ENABLE);

}


void TIM2_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE,GPIO_Pin_9);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE,GPIO_Pin_11);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_10);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_11);

TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);

TIM_Cmd(TIM2, ENABLE);
TIM_Cmd(TIM1, ENABLE);

}


void qianjin()
{
PDout(12,1);
PDout(13,0);
PDout(14,1);
PDout(15,0);
PEout(9,1);
PEout(11,0);
PBout(10,1);
PBout(11,0);
}

void houtui()
{
PDout(12,0);
PDout(13,1);
PDout(14,0);
PDout(15,1);
PEout(9,0);
PEout(11,1);
PBout(10,0);
PBout(11,1);
}

void stop()
{
PDout(12,0);
PDout(13,0);
PDout(14,0);
PDout(15,0);
PEout(9,0);
PEout(11,0);
PBout(10,0);
PBout(11,0);
}

结合任务二的部分,设计出简单的循迹算法

任务实现

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#include "stm32f10x.h"                  // Device header
#include "AD.h"
#include "contral car.h"
#include "PWM.h"
#include "OLED.h"
int main(void)
{
TIM4_PWM_Init(450,7199);
TIM2_PWM_Init(450,7199);
AD_Init();
OLED_Init();
while(1)
{
AD_GetValue();
OLED_ShowNum(1,1,IPT[0],1);
OLED_ShowNum(2,1,IPT[1],1);
OLED_ShowNum(3,1,IPT[2],1);
OLED_ShowNum(4,1,IPT[3],1);
OLED_ShowNum(1,4,IPT[4],1);

//直行
if(IPT[0]==0&&IPT[1]==0&&IPT[2]==1&&IPT[3]==0&&IPT[4]==0)
{
qianjin();
}
//左偏一点点
else if(IPT[0]==0&&IPT[1]==1&&IPT[2]==0&&IPT[3]==0&&IPT[4]==0)

{
turnleft(30);
} //右偏一点点
else if(IPT[0]==0&&IPT[1]==0&&IPT[2]==0&&IPT[3]==1&&IPT[4]==0)

{
turnright(30);
} //左转
else if(IPT[0]==1&&IPT[1]==1&&IPT[3]==0&&IPT[4]==0)

{
turnleft(90);
} //右转

else if(IPT[0]==0&&IPT[1]==0&&IPT[3]==1&&IPT[4]==1)

{
turnright(90);
} //转弯
else if(IPT[0]==0&&IPT[1]==0&&IPT[2]==0&&IPT[3]==0&&IPT[4]==0)

{
turnright(180);
} else if(IPT[0]==1&&IPT[1]==1&&IPT[2]==1&&IPT[3]==1&&IPT[4]==1)

{
turnright(90);
}

}




}
#include "stm32f10x.h" // Device header

uint16_t AD_Value[5];
uint16_t tixed_value1=1000;
uint16_t tixed_value2=500;
uint16_t IPT[5];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3,ENABLE);//开启ADC1和GPIOC的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOF,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);//开启时钟


RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频,分频之后ADCCLK=72MHz/6=12

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_8|GPIO_Pin_10|GPIO_Pin_9;
GPIO_Init(GPIOF, &GPIO_InitStructure);


ADC_RegularChannelConfig(ADC3,ADC_Channel_4,1,ADC_SampleTime_13Cycles5);//规则组序列3的位置,配置为通道

ADC_RegularChannelConfig(ADC3,ADC_Channel_6,2,ADC_SampleTime_13Cycles5);//规则组序列3的位置,配置为通道
ADC_RegularChannelConfig(ADC3,ADC_Channel_7,3,ADC_SampleTime_13Cycles5);//规则组序列3的位置,配置为通道
ADC_RegularChannelConfig(ADC3,ADC_Channel_8,4,ADC_SampleTime_13Cycles5);//规则组序列3的位置,配置为通道

ADC_RegularChannelConfig(ADC3,ADC_Channel_12,5,ADC_SampleTime_13Cycles5);//规则组序列1的位置,配置为通道






ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE ;
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//外部触发,使用软件触发,不需要外部触发
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_NbrOfChannel=5 ; //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
ADC_InitStructure.ADC_ScanConvMode=ENABLE ; //扫描模式
ADC_Init(ADC3,&ADC_InitStructure);





DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_BufferSize=5; //转运的数据大小(转运次数)
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //数据传输方向,选择由外设到存储器
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable; //硬件触发或者软件触发,该处选择软件触发
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)AD_Value; //存储器初始地址,给定形参AddrB
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //存储器数据宽度,选择字节
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //存储器地址自增,选择使能
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //模式,选择正常模式
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&ADC3->DR; //外设初始地址,给定形参AddrA
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //外设数据宽度,选择字节
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设地址自增,选择使能
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //优先级,选择中等
DMA_Init(DMA2_Channel5,&DMA_InitStructure);



DMA_Cmd(DMA2_Channel5,ENABLE);
ADC_DMACmd(ADC3 ,ENABLE );
ADC_Cmd(ADC3,ENABLE);

ADC_ResetCalibration(ADC3); //校准
while(ADC_GetResetCalibrationStatus(ADC3)==SET);
ADC_StartCalibration(ADC3);
while(ADC_GetCalibrationStatus(ADC3)==SET);




}

void AD_GetValue(void)
{
DMA_Cmd(DMA2_Channel5,DISABLE);
DMA_SetCurrDataCounter (DMA2_Channel5,5);
DMA_Cmd(DMA2_Channel5,ENABLE);

ADC_SoftwareStartConvCmd(ADC3,ENABLE); //软件触发AD转换一次

while(DMA_GetFlagStatus(DMA2_FLAG_TC5)==RESET);
DMA_ClearFlag(DMA2_FLAG_TC5);

//将ADC值转换为黑线1或者没有线0,红外对管从左往右分别为IPT0,IPT1,IPT2,IPT3,IPT4
uint8_t i;
for (i=0;i<=7;i++)
{
if(AD_Value[i]>=tixed_value1)
{
IPT[i]=1;
}else if (AD_Value[i]<=tixed_value2) {
IPT[i]=0;
}else IPT[i]=2;

}

}
#include "stm32f10x.h" // Device header

void my_set_tim_compare(uint16_t brr)
{
TIM_SetCompare2(TIM4,brr);
TIM_SetCompare1(TIM4,brr);
TIM_SetCompare2(TIM2,brr);
TIM_SetCompare1(TIM2,brr);
TIM_SetCompare2(TIM1,brr);
TIM_SetCompare1(TIM1,brr);
}

void PDout(uint8_t pin, uint8_t value) {
if (value) {
GPIO_SetBits(GPIOD, 1 << pin); // Set the pin high
} else {
GPIO_ResetBits(GPIOD, 1 << pin); // Set the pin low
}
}

void PEout(uint8_t pin, uint8_t value) {
if (value) {
GPIO_SetBits(GPIOE, 1 << pin); // Set the pin high
} else {
GPIO_ResetBits(GPIOE, 1 << pin); // Set the pin low
}
}

void PBout(uint8_t pin, uint8_t value) {
if (value) {
GPIO_SetBits(GPIOB, 1 << pin); // Set the pin high
} else {
GPIO_ResetBits(GPIOB, 1 << pin); // Set the pin low
}
}


void TIM4_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;



RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);



GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//TIM4通道2

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//TIM4通道1

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_12);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_13);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_14);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD,GPIO_Pin_15);

TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM4, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);


TIM_Cmd(TIM4, ENABLE);

}


void TIM2_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE,GPIO_Pin_9);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE,GPIO_Pin_11);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_10);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_11);

TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);

TIM_Cmd(TIM2, ENABLE);
TIM_Cmd(TIM1, ENABLE);

}


void qianjin()
{
PDout(12,1);
PDout(13,0);
PDout(14,1);
PDout(15,0);
PEout(9,1);
PEout(11,0);
PBout(10,1);
PBout(11,0);
}

void houtui()
{
PDout(12,0);
PDout(13,1);
PDout(14,0);
PDout(15,1);
PEout(9,0);
PEout(11,1);
PBout(10,0);
PBout(11,1);
}

void stop()
{
PDout(12,0);
PDout(13,0);
PDout(14,0);
PDout(15,0);
PEout(9,0);
PEout(11,0);
PBout(10,0);
PBout(11,0);
}
#include "stm32f10x.h" // Device header
#include "PWM.h"
#include "Delay.h"

void leftforward(void)
{
PDout(12,1);
PDout(13,0);
PDout(14,1);
PDout(15,0);
}

void rightforward(void)
{
PEout(9,1);
PEout(11,0);
PBout(10,1);
PBout(11,0);
}

void leftbackward(void)
{
PDout(12,0);
PDout(13,1);
PDout(14,0);
PDout(15,1);
}

void rightbackward(void)
{
PEout(9,0);
PEout(11,1);
PBout(10,0);
PBout(11,1);
}

void turnright(uint16_t angle)
{
int dt = (8*angle);
leftforward();
rightbackward();
Delay_ms(dt);

}

void turnleft(uint16_t angle)
{
int dt = (8*angle);
leftbackward();
rightforward();
Delay_ms(dt);

}

视频:

任务五

并联比较型ADC概述

并联比较型ADC是通过并行比较多个电压输入值,将输入模拟信号转换为数字信号的一种方式,基本思想是同时将输入电压与多个参考电压进行比较,从而快速确定输入电压的数字表示。

各部分功能

  1. 比较器:主要功能是将输入的模拟信号与一组预设的参考电压进行比较。比较器输出高电平或低电平,表示输入信号高于或低于参考电压。
  2. 参考电压源:提供一组稳定的参考电压,通常是从0V到最高输入电压(例如,3.3V或5V)。这组参考电压可以是均匀分布的,帮助比较器进行比较。
  3. 编码器:根据比较器的输出,将比较结果转换为相应的二进制数字。如果比较器的输出表示有几个高电平,编码器就会确定输入信号的数值。
  4. 触发器:在完成比较后,触发器用来锁存比较器的结果,确保在ADC转换完成之前,不会受到输入信号变化的影响。
  5. 门电路:控制ADC的工作状态,例如使能和禁用ADC的功能。

工作原理

  1. 输入信号采样:首先,ADC从输入信号(模拟电压)进行采样。
  2. 并行比较:模拟信号会同时与多个参考电压进行比较,由多个比较器并行处理。
  3. 输出编码:根据比较器的结果,编码器将输出对应的数字信号。
  4. 结果输出:最终,转换后的数字信号被输出,通常是二进制格式,以供后续数字处理。