使用stm32f1系列的芯片
使用时建议搭配参考手册、数据手册和f1固件库使用手册一起食用
这篇文章会持续更新(大概)
外部中断
使用标准库还是得多查数据手册
static void ConfigEXTI(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO_InitStructure用于存放GPIO的参数
EXTI_InitTypeDef EXTI_InitStructure; //EXTI_InitStructure用于存放EXTI的参数
NVIC_InitTypeDef NVIC_InitStructure; //NVIC_InitStructure用于存放NVIC的参数
//使能RCC相关时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA的时钟
//配置PA8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置输入上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置PA8的EXTI和NVIC
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8); //选择引脚作为中断线
EXTI_InitStructure.EXTI_Line = EXTI_Line8; //选择中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //开放中断请求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //设置为上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断线
EXTI_Init(&EXTI_InitStructure); //根据参数初始化EXTI
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //中断通道号
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断
NVIC_Init(&NVIC_InitStructure); //根据参数初始化NVIC
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line8) != RESET) //判断中断是否发生
{
/*********/
//用户任务函数
/*********/
EXTI_ClearITPendingBit(EXTI_Line8);
}
}
PWM
static void ConfigTimer4CH1forPWM1(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; //用于存放定时器通道引脚的参数
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//TIM_TimeBaseStructure用于存放定时器的参数
TIM_OCInitTypeDef TIM_OCInitStructure;//用于存放定时器输出比较的参数
//使能RCC相关时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能TIM2的时钟
// 配置PWM引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //设置引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置TIM2
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置预分频器值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:tDTS = tCK_INT
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据参数初始化定时器
// 配置PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM 脉冲宽度调制模式 1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出状态使能
TIM_OCInitStructure.TIM_Pulse = 0; // 设置初始占空比为0%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 设置优先级
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_Cmd(TIM4, ENABLE); //使能定时器
TIM_CtrlPWMOutputs(TIM4, ENABLE); //使能PWM输出
}
void ConfigDMA1CH1forTIM4CH1(u16 *buffer)
{
DMA_InitTypeDef DMA_InitStruct; // 用于存放DMA的参数
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA1的时钟
//配置DMA
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&TIM4->CCR1; //外设地址
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)buffer; //内存地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; //外设为目标
DMA_InitStruct.DMA_BufferSize = DATA_SIZE; //内存数据大小
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //关闭外设地址增量
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //打开内存地址增量
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据大小为半字
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据大小为半字
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //DMA循环模式
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //设置优先级
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //关闭DMA的内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA传输
TIM_DMACmd(TIM4, TIM_DMA_CC1, ENABLE); //使能TIM4的DMA请求
}
注意:使用DMA时,DMA传输方向要弄清楚,是内存到外设,还是外设到内存,还是内存到内存,内存地址为数据的的地址,外设地址为传出或传入数据的外设的数据寄存器,例如TIMx->CCRx为包含了装入当前捕获/比较x寄存器的值(预装载值)。
使能外设的DMA请求后DMA就一直在传数据给外设了,例中将内存数据传给PWM改变占空比,但传输速度比PWM的周期要快,会导致占空比更新但无作用,需校准DMA传输周期和PWM周期
//例如PWM周期为1ms,开一个1ms的中断函数去开关一次DMA传输
void SetPWMPulse(u16 cnt)
{
static u16 s_iCnt;
s_iCnt++;
if(s_iCnt >= cnt)
{
s_iCnt = 0;
DMA_Cmd(DMA1_Channel1, ENABLE); // 启动DMA传输
DMA_Cmd(DMA1_Channel1, DISABLE); // 关闭DMA传输
}
}
#define DATA_SIZE 5
u16 data[DATA_SIZE]={100,200,300,400,500};
int main(void)
{
//用户其他初始化
//************//
ConfigTimer4CH1forPWM1(999,71);
ConfigDMA1Channel1(data);
while(1)
{
}
}
void SysTick_Handler(void)
{
TimDelayDec(); //延时计数函数
SetPWMPulse(1);
}
DAC
void ConfigDAC1(void)
{
DAC_InitTypeDef DAC_InitStruct; //用于存放DAC的参数
GPIO_InitTypeDef GPIO_InitStruct; //用于存放DAC通道引脚的参数
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); //使能DAC的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA的时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; //设置DAC通道引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; //设置引脚模式
GPIO_Init(GPIOA, &GPIO_InitStruct);
DAC_InitStruct.DAC_Trigger = DAC_Trigger_Software; //软件触发DAC
DAC_InitStruct.DAC_WaveGeneration = DAC_WaveGeneration_None; //无波形产生
DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_4095; //DAC产生最大幅度为4095
DAC_InitStruct.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //使能输出缓存
DAC_Init(DAC_Channel_1, &DAC_InitStruct);
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //设置DAC通道输出值
DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE); //使能DAC软件触发
}
注意:DAC的触发选择可以是无触发、定时器事件触发、外部中断触发和软件触发
无触发时当设置了输出值就可以立刻改变输出值,而软件触发的方式必须要重新使能一次
定时器事件触发和外部中断触发可以配合DMA使用
void DAC_SetOutputValue(u32 DAC_Channel, u16 value)
{
DAC_SetChannel1Data(DAC_Align_12b_R, value);
DAC_SoftwareTriggerCmd(DAC_Channel, ENABLE);
}
void DAC_ControlLED(u16 cnt) //呼吸灯
{
static u16 s_iCnt, flag, value = 2047;
s_iCnt++;
if(s_iCnt >= cnt)
{
s_iCnt = 0;
if(flag == 0) value += 24;
else if(flag == 1) value -= 24;
DAC_SetOutputValue(DAC_Channel_1, value);
if(value > 4071) flag = 1;
else if(value < 2047) flag = 0;
printf("DAC Vol = %d\r\n",DAC_GetDataOutputValue(DAC_Channel_1));
}
}
int main(void)
{
//用户其他初始化
//************//
ConfigDAC1();
while(1)
{
}
}
void SysTick_Handler(void)
{
TimDelayDec(); //延时计数函数
DAC_ControlLED(50); //50ms执行一次
}
ADC
多通道DMA传输
#define ADC_Channel_Num 2 //ADC通道数量
u16 arr_ADCbuf[2] = {0}; //采样值缓冲数组
static void ConfigADC(void)
{
ADC_InitTypeDef ADC_InitStruct; //用于存放ADC的参数
GPIO_InitTypeDef GPIO_InitStruct; //用于存放GPIO的参数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA的时钟
//配置GPIO
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; //配置GPIO模式为模拟输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2; //配置引脚
GPIO_Init(GPIOA, &GPIO_InitStruct);
//配置ADC
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; //ADC为独立模式
ADC_InitStruct.ADC_ScanConvMode = ENABLE; //使能扫描转换模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //使能连续转换模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //无外部触发转换
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //ADC采集数据右对齐
ADC_InitStruct.ADC_NbrOfChannel = 2; //通道数量2
ADC_Init(ADC1, &ADC_InitStruct);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); //配置ADC1_CH1排名1采样时间239.5个周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_239Cycles5); //配置ADC1_CH2排名2采样时间239.5个周期
ADC_Cmd(ADC1, ENABLE); //使能ADC
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能ADC软件启动转换
ADC_DMACmd(ADC1, ENABLE); //使能ADC的DMA传输
}
static void ConfigDMA1forADC1CH1andCH2(u16 *buffer)
{
DMA_InitTypeDef DMA_InitStruct; //用于存放DMA的参数
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA时钟
//配置DMA
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设地址
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)buffer; //内存地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //外设为数据传输源
DMA_InitStruct.DMA_BufferSize = ADC_Channel_Num; //内存数据大小
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//关闭外设地址增量
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //打开内存地址增量
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据类型为半字
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据类型为半字
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //DMA循环模式
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //高优先级
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //关闭内存到内存
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel1, ENABLE); // 启动DMA传输
}
uint16_t GetADC1Value(uint8_t ADC_Channel)
{
return arr_ADCbuf[ADC_Channel-1];
}
int main(void)
{
//用户其他初始化
//************//
ConfigADC();
ConfigDMA1forADC1CH1andCH2(arr_ADCbuf);
while(1)
{
}
}
void SysTick_Handler(void)
{
TimDelayDec(); //延时计数函数
if(uwTick % 200 == 0)
printf("adc_ch1 = %d adc_ch2 = %d\r\n", GetADC1Value(ADC_Channel_1), GetADC1Value(ADC_Channel_2));
}
对于多通道的ADC采样,若不使用DMA缓存采样的数据,也可以通过使能软件触发转换的时间或其他方式的触发转换方式来得到不同通道采样的数据
双ADC传输
static void ConfigADC1andADC2(void)
{
ADC_InitTypeDef ADC_InitStruct; //用于存放ADC的参数
GPIO_InitTypeDef GPIO_InitStruct; //用于存放GPIO的参数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE); //使能ADC1和ADC2的时钟
//配置GPIO
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; //模拟输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2; //配置引脚
GPIO_Init(GPIOA, &GPIO_InitStruct);
//配置ADC
ADC_InitStruct.ADC_Mode = ADC_Mode_RegSimult; //ADC1 和 ADC2 工作在同步规则模式
ADC_InitStruct.ADC_ScanConvMode = DISABLE; //失能扫描转换模式
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; //失能连续转换模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //无外部触发转换
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //ADC采集数据右对齐
ADC_InitStruct.ADC_NbrOfChannel = 1; //通道数量1
ADC_Init(ADC1, &ADC_InitStruct);
ADC_Init(ADC2, &ADC_InitStruct);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5); //配置ADC1_CH1排名1采样时间239.5个周期
ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 1, ADC_SampleTime_239Cycles5); //配置ADC2_CH2排名1采样时间239.5个周期
// ADC_SoftwareStartConvCmd(ADC1, ENABLE);
ADC_ExternalTrigConvCmd(ADC2, ENABLE); //使能ADC2外部触发转换
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_Cmd(ADC2, ENABLE); //使能ADC1
ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA传输
}
u32 ADC_ConvertedValue;
int main(void)
{
//用户其他初始化
//************//
ConfigADC1andADC2();
while(1)
{
}
}
void SysTick_Handler(void)
{
TimDelayDec(); //延时计数函数
if(uwTick % 1000 == 0)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
ADC_ConvertedValue = ADC1->DR;
printf("adc1 = %d adc2 = %d\r\n", (ADC_ConvertedValue&0xffff), ((ADC_ConvertedValue>> 16)&0xffff));
}
}
注意:如果ADC1使用软件触发,ADC2则使用外部通道触发;ADC1使用外部事件触发时,ADC2设置成软件触发,这样可以防止意外触发从转换。
在双ADC模式中,ADC1和ADC2的规则通道转换数据均会保存到主数据寄存器,也就是ADC1数据寄存器(ADC1_DR)中。为了能在主寄存器中读取从ADC的转换数据,必须使能DMA位。 无论是否使用DMA传输规则通道数据 。