实验任务
利用线程间同步-事件集机制,实现当一个ADC通道采样值大于3V 或者另外一个ADC通道采样值小于0.5V时,实现报警功能。ADC通道自由选择。
实验目的
熟悉RT-Thread内核事件集的使用,熟悉STM32 ADC外设多通道采集的使用。
实验环境
1、硬件环境:野火STM32霸道开发板
2、软件环境:RT-Thread Nano 3.1.3,MDK 5.25
实验步骤
1、查看开发板原理图可用的ADC资源,这里选择使用PC1引脚和PC3引脚进行实验。
2、根据原理图上的引脚去STM32的数据手册获取ADC的通道。
3、编写驱动程序
bsp_adc.h文件
#ifndef __ADC_H
#define __ADC_H
// 转换通道个数
#define NOFCHANEL 2
void ADC_Config(void);
void adc_value_update(void *parameter);
#endif /* __ADC_H */
bsp_adc.c文件
#include "config.h"
#include "bsp_adc.h"
__IO uint16_t ADC_ConvertedValue[NOFCHANEL]= {0,0};
static void ADC_GPIO_Config(void)
{
GPIO_InitTypeDef g;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
g.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3;
g.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &g);
}
static void ADC_Mode_Config(void)
{
DMA_InitTypeDef d;
ADC_InitTypeDef a;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* ADC-DMA初始化 */
DMA_DeInit(DMA1_Channel1);
d.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));
d.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
d.DMA_DIR = DMA_DIR_PeripheralSRC;
d.DMA_BufferSize = NOFCHANEL;
d.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
d.DMA_MemoryInc = DMA_MemoryInc_Enable;
d.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
d.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
d.DMA_Mode = DMA_Mode_Circular;
d.DMA_Priority = DMA_Priority_High;
d.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &d);
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC初始化 */
ADC_DeInit(ADC1);
a.ADC_Mode = ADC_Mode_Independent;
a.ADC_ScanConvMode = ENABLE;
a.ADC_ContinuousConvMode = ENABLE;
a.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
a.ADC_DataAlign = ADC_DataAlign_Right;
a.ADC_NbrOfChannel = NOFCHANEL;
ADC_Init(ADC1, &a);
/* 配置ADC时钟PCLK2的8分频,即9MHz */
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/* 配置ADC 通道的转换顺序和采样时间 */
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 2, ADC_SampleTime_55Cycles5);
/* 使能ADC的DMA请求,打开ADC */
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); // 初始化ADC 校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); // 等待校准寄存器初始化完成
ADC_StartCalibration(ADC1); // ADC开始校准
while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 由于没有采用外部触发,所以使用软件触发ADC转换
}
void ADC_Config(void)
{
ADC_GPIO_Config();
ADC_Mode_Config();
}
4、创建ADC报警线程
TaskStruct TaskThreads[] = {
{"iwdg_thread", iwdg_thread_entry, RT_NULL, 512, 15, 10},
{"led_thread", led_thread_entry, RT_NULL, 512, 15, 10},
{"usart2_recv_thread", usart2_recv_thread_entry, RT_NULL, 512, 2, 10},
{"adc_detect_thread", adc_detect_thread_entry, RT_NULL, 512, 5, 10},/* ADC报警线程 */
/*********************************************************/
//用户添加线程参数
//例如:{线程名字,线程入口函数,线程入口函数参数,线程栈大小,线程的优先级,线程时间片},
{" ", RT_NULL, RT_NULL, RT_NULL, RT_NULL, RT_NULL},
};
void adc_detect_thread_entry(void *parameter)
{
rt_err_t res;
rt_uint32_t e;
rt_timer_t adc_timer;
/* 创建软件定时器,用于定时获取ADC数据 */
adc_timer = rt_timer_create("adc_timer",
adc_value_update,
RT_NULL,
1000,
RT_TIMER_FLAG_PERIODIC);
if( adc_timer != RT_NULL )
rt_timer_start(adc_timer);
else
rt_kprintf("adc_timer 创建失败!\n");
/* 创建ADC报警事件集 */
adc_alarm_event = rt_event_create("adc_alarm_event", RT_IPC_FLAG_FIFO);
if( adc_alarm_event == RT_NULL )
rt_kprintf("adc_alarm_event 创建失败!\n");
while(1)
{
res = rt_event_recv(adc_alarm_event,
EVENT_FLAG1 | EVENT_FLAG3,/* 关注EVENT_FLAG1和EVENT_FLAG3事件 */
RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,/* EVENT_FLAG1和EVENT_FLAG3事件逻辑或关系,接受到事件以后清除标记 */
RT_WAITING_FOREVER,
&e);
if( res == RT_EOK )
{
if( e & EVENT_FLAG1 )
rt_kprintf("警报!PC1引脚电压小于0.5V\n");
if( e & EVENT_FLAG3 )
rt_kprintf("警报!PC3引脚电压大于3.0V\n");
//BeepOn();
}
else
{
rt_kprintf("事件集接收发生错误!\n");
}
}
}
5、编写定时器周期回调函数
void adc_value_update(void *parameter)
{
float ADC_ConvertedValueLocal[NOFCHANEL];
rt_interrupt_enter();
ADC_ConvertedValueLocal[0] = (float)ADC_ConvertedValue[0]/4096*3.3;
ADC_ConvertedValueLocal[1] = (float)ADC_ConvertedValue[1]/4096*3.3;
rt_interrupt_leave();
// rt_kprintf("PC1 = %d \n", ADC_ConvertedValue[0]);
// rt_kprintf("PC3 = %d \n", ADC_ConvertedValue[1]);
if( ADC_ConvertedValueLocal[0] < 0.5 )//PC1电位器小于0.5V
rt_event_send(adc_alarm_event, EVENT_FLAG1);
if( ADC_ConvertedValueLocal[1] > 3.0 )//PC3大于3.0V
rt_event_send(adc_alarm_event, EVENT_FLAG3);
}
测试结果
当PC1电压值小于0.5V或者PC3大于3.0V时,串口调试助手会发出警报。