"); //-->
电源控制
先看一下这2张截自STM32参考手册的图片:
下面这张表截自STM32F103x8/B的数据手册,对上图的参数给出了具体数值:
下面对上面2张图和表格中的数据做一个简要的解释:
1)PVD = Programmable Votage Detector 可编程电压监测器
它的作用是监视供电电压,在供电电压下降到给定的阀值以下时,产生一个中断,通知软件做紧急处理。在给出表格的上半部分就是可编程的监视阀值数据。当供电电压又恢复到给定的阀值以上时,也会产生一个中断,通知软件供电恢复。供电下降的阀值与供电上升的PVD阀值有一个固定的差值,这就是表中的VPVDhyst(PVD迟滞)这个参数,通过列出的PVD阀值数据可以看到这个差别。引入这个差值的目的是为了防止电压在阀值上下小幅抖动,而频繁地产生中断。
2)POR = Power On Reset 上电复位;PDR = Power Down Reset 掉电复位。
POR的功能是在VDD电压由低向高上升越过规定的阀值之前,保持芯片复位,当越过这个阀值后的一小段时间后(图中的"滞后时间"或表中的"复位迟滞"),结束复位并取复位向量,开始执行指令。这个阀值就是表中倒数第4行(min=1.8V,typ=1.88V,max=1.96V)。
POR的功能是在VDD电压由高向低下降越过规定的阀值后,将在芯片内部产生复位,这个阀值就是表中倒数第3行(min=1.84V,typ=1.92V,max=2.0V)。
3)可以看到POR比PDR大了0.04V,这就是表中倒数第2行,VPDRhyst(PDR迟滞)=40mV。
4)从上面的第2张图可以看到,当VDD上升越过POR阀值时,内部并不马上结束复位,而是等待一小段时间(Reset temporization),这就是表中的最后一行TRSTTEMPO,它的典型数值是2.5ms。
这个滞后时间是为了等待供电电压能够升高到最低可靠工作电压以上,我们看到POR阀值最小只有1.8V,最大也只有1.96V,都低于数据手册中给出的最低可靠工作电压2.0V,所以这个滞后时间是十分必要的,如果供电电压上升缓慢,尤其是从1.8V升到2.0V以上超过1~2.5ms,则很可能造成上电复位后MCU不能正常工作的情况。
STM32--低功耗模式 收藏
STM32F10xxx有三中低功耗模式:
●睡眠模式(Cortex?-M3内核停止,外设仍在运行)
●停止模式(所有的时钟都以停止)
●待机模式(1.8V电源关闭)
待机模式可实现系统的最低功耗。 可将电流消耗降至两微安。
在待机模式下,所有的I/O引脚处于高阻态,除了以下的引脚:
●复位引脚(始终有效)
●当被设置为防侵入或校准输出时的TAMPER引脚
●被使能的唤醒引脚
时钟频率72MHz时,从闪存执行代码,STM32功耗36mA,是32位市场上功耗最低的产品,相当于0.5mA/MHz。
/*按钮GPIOB9进入睡眠,WKUP pin(GPIOA0)唤醒,GPIOD3-LED 200ms闪烁*/
int main(void)
{
/* System Clocks Configuration **********************************************/
RCC_Configuration();
GPIO_Configuration();
/* Enable PWR and BKP clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* Enable WKUP pin */
PWR_WakeUpPinCmd(ENABLE);
/* Allow access to BKP Domain */
PWR_BackupAccessCmd(ENABLE);
//RTC_Configuration();
EXTI_Configuration();
NVIC_Configuration();
SysTick_Config(SystemFrequency / 1000 *200 ); //200ms
while (1)
{
Delay(0xAFFFF);
}
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zyboy2000/archive/2009/10/16/4681682.aspx
STM32启动代码分析问题 收藏
能否讲解一下startup_stm32f10x_cl.s启动代码含义,谢谢!
我现在看反汇编如下
0x08000000 0678 LSLS r0,r7,#25
0x08000002 2000 MOVS r0,#0x00
0x08000004 1105 ASRS r5,r0,#408
0x08000006 0800 LSRS r0,r0,#00A
。。。。。。。。。。。。。。。。。。。。。。
上面应该对应
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
0x08001104 4808 LDR r0,[pc,#32] ; 程序一运行跳到这里,why?
0x08001106 4700 BX r0,r0,#0
上面对应
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
LDR R0, =__main
BX R0
ENDP
那位能说一下为什么跳到0x08001104,即PC =0x08001104, 我想应该PC应该先跳到0x08000000?
解答:
cortex-M3和ARM9的架构有很大区别,ARM7、ARM9在复位后是从地址0处开始执行指令,也就是说地址0x00000000的内容是指令。而cortex-M3的异常向量表中的内容并不是指令,0x00000000处(当然也可能映射到别的范围)是主堆栈指针的数值,0x00000004的内容是复位后需要跳转到的地址,是一个地址而不是一条指令。
stm32选择flash启动方式,中断向量表映射到0x08000000,由楼主给出的反汇编可知,复位后主堆栈指针的位置是0x20000678,0x08000004位置的数值是0x08001105,由于cortex-M3只能运行在thumb2状态,所以要保证向PC(R15)写入的数值的bit0必须是1(如果向PC写入的数值的bit0是0,则处理器认为试图切入ARM状态,会产生fault),而实际上stm32的指令是半字对齐的,所以复位后会跳转到0x08001104.
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zyboy2000/archive/2009/10/15/4674008.aspx
STM32开发板基础教程(十) - RTC初探(转帖) 收藏
豆皮 - STM32开发板基础教程(十) - RTC初探(原创)
版权所有 STMFANS 原创,转载请保留出处
http://www.stmfans.com/bbs/viewthread.php?tid=1147&extra=page%3D1
STM32的RTC实际是一个独立的定时器。
下面将介绍如何使用RTC。
我们将头一次牵扯到振源的问题。
首先介绍一下STM32使用的各种振源。
有三种
HSE: 外置晶振
HSI: 内置RC振荡
LSE: 外置RTC振荡(32768居多)
APB1 和 APB2 是经过PLL以后的振荡源。
STM32启动,首先使用的HSI振荡,在确认HSE振荡可用的情况下,才可以转而使用HSE,
当HSE出现问题,STM32可自动切换回HSI振荡,维持工作。
LSE振荡则是专门供RTC使用。
LSE晶振需要特别注意。
STM32非常奇怪,要求使用 6p负载的晶振,
市面买到的时钟晶振,绝大多是是12.5pF的
算是一个不小的bug,
大家做相关开发的时候,要留神。
要买6pF的晶振,配10pF的谐振电容。
由此,马七怀念一下AVR单片机。不用谐振电容都跑的飞,当然是Mega系列。
下面介绍RTC驱动过程。
第一件事情,喂时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR, ENABLE);
注意,喂的是什么?不是RTC,是电源管理和BKP备份器的时钟。用于备份模式下。
即系统掉电了,BKP和RTC还能继续工作,RTC继续计时。
那么RTC的时钟呢?前面提到,RTC的时钟,一般用LSE。
第二件事情,初始化RTC
// RTC config
void RTC_configuration()
{
//Open the BKP
PWR_BackupAccessCmd(ENABLE);
BKP_DeInit();
//RTC use the LSE Clock
RCC_LSEConfig(RCC_LSE_ON); //RCC打开了LSE时钟
//Wait LSE Ready
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET); //等待LSE就绪,一般来说,如果谐振不对,就会死在这里。实
际代码请慎重
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //RTC使用时钟,可以使用LSE,也可以使用HSI,也可以使用HSE/128
RCC_RTCCLKCmd(ENABLE); //RTC的时钟开启
RTC_WaitForSynchro(); //RTC等待同步,
RTC_WaitForLastTask(); //这个代码在RTC中常常出现,类似于等待就绪的含义
// Interrupt Each Second
RTC_ITConfig(RTC_IT_SEC, ENABLE); //RTC开中断,RTC中断有三种,秒中断,闹钟中断,溢出中断,很明显他们的作用。秒中断用于即时操作,闹钟中断用于关闭或者唤醒,溢出中断的话,用于复位RTC
RTC_WaitForLastTask();//
RTC_SetPrescaler(32767); //RTC预分频,32768HZ,分为一秒一个振荡,RTC period = RTCCLK/RTC_PR = (32.768
KHz)/(32767+1)
RTC_WaitForLastTask(); //等待同步
}
这样,RTC就启动了。
通过 RTC_GetCounter() 这个函数。读到计数器的值。
既然 一秒增一个。
很容易就可以从 计数器的值,算出确切的时间值。
对于这种时间分量复杂的,我习惯用结构体定义
typedef struct
{
unsigned char Sec;
unsigned char Min;
unsigned char Hour;
unsigned char Day;
unsigned char Month;
unsigned char Year;
}Time_Struct;
// translate seconds to YY::MM:D::HH::MM::SS
Time_Struct read_RTC_time()
{
unsigned long Time_Value;
Time_Struct TimeStruct;
Time_Value = RTC_GetCounter();
TimeStruct.Year = Time_Value/(12*30*24*3600);
TimeStruct.Month = Time_Value/(30*24*3600) - TimeStruct.Year*12;
TimeStruct.Day = Time_Value/(24*3600) - TimeStruct.Year*12*30 - TimeStruct.Month*30;
TimeStruct.Hour = Time_Value/3600 - TimeStruct.Year*12*30*24 - TimeStruct.Month*30*24 - TimeStruct.Day*24;
TimeStruct.Min = Time_Value/60 - TimeStruct.Year*12*30*24*60 - TimeStruct.Month*30*24*60 -
TimeStruct.Day*24*60 - TimeStruct.Hour*60;
TimeStruct.Sec = Time_Value - TimeStruct.Year*12*30*24*60*60 - TimeStruct.Month*30*24*60*60 -
TimeStruct.Day*24*60*60 - TimeStruct.Hour*60*60 -TimeStruct.Min*60;
return TimeStruct;
}
当然,也可以在任意时候设置这个时间,手工修改Counter即可。相关函数在工程文件rtc.c当中。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zyboy2000/archive/2009/09/26/4598602.aspx
STM32多通道ADC规则转换实现了(转)! 收藏
vu16 ADC_RCVTab[160] ; //自己添加
/*******************************************************************************
* Function Name : main
* Description : Main program
* Input : None
* Output : None
* Return : None
*******************************************************************************/
int main(void)
{
#ifdef DEBUG
debug();
#endif
/* System clocks configuration ---------------------------------------------*/
RCC_Configuration();
/* NVIC configuration ------------------------------------------------------*/
NVIC_Configuration();
/* GPIO configuration ------------------------------------------------------*/
GPIO_Configuration();
LcdShow_Init();
/* DMA1 channel1 configuration ----------------------------------------------*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_RCVTab;//内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//dma传输方向单向
DMA_InitStructure.DMA_BufferSize = 160;//设置DMA在传输时缓冲区的长度 word
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA的外设递增模式,一个外设
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//设置DMA的内存递增模式,
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据字长
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存数据字长
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//设置DMA的传输模式:连续不断的循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//设置DMA的优先级别
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//设置DMA的2个memory中的变量互相访问
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立工作模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描方式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发禁止
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 8;//用于转换的通道数
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channels configuration [规则模式通道配置]*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_8 , 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_9 , 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 3, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 4, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 5, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 6, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 7, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 8, ADC_SampleTime_239Cycles5);
/* Enable ADC1 DMA [使能ADC1 DMA]*/
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 [使能ADC1]*/
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(RESET==DMA_GetFlagStatus(DMA1_FLAG_TC1)); //自己添加
while(1)
{
vu16 value1 = 0;
vu16 value2 = 0;
vu16 value3 = 0;
vu16 value4 = 0;
vu16 value5 = 0;
vu16 value6 = 0;
vu16 value7 = 0;
vu16 value8 = 0;
value1 = average(ADC_RCVTab,0);
value2 = average(ADC_RCVTab,1);
value3 = average(ADC_RCVTab,2);
value4 = average(ADC_RCVTab,3);
value5 = average(ADC_RCVTab,4);
value6 = average(ADC_RCVTab,5);
value7 = average(ADC_RCVTab,6);
value8 = average(ADC_RCVTab,7);
u8 num1 = value3 % 10;
u8 num2 = (value3 / 10) % 10;
u8 num3= (value3 / 100) % 10;
u8 num4 = value3 / 1000;
if (num1 > 9)
display[3] = num1 + (65 - 10);
else
display[3] = num1 + (48-0);
if (num2 > 9)
display[2] = num2 +(65 - 10);
else
display[2] = num2 + (48 - 0);
if (num3>9)
display[1]=num3+(65-10);
else
display[1]=num3+(48-0);
if (num4>9)
display[0]=num4+(65-10);
else
display[0]=num4+(48-0);
write_string(display);
delay();
}
}
u16 average(vu16 ADCDataTab[], u16 nChannel) //自己添加
{
u16 averagevalue="0", maxvalue="0", minvalue="0xFFFF", i;
for (i=0;i<20;i++)
{
averagevalue += *(ADCDataTab+nChannel+i*8);
if(*(ADCDataTab+nChannel+i*8)>maxvalue)
maxvalue=*(ADCDataTab+nChannel+i*8);
if(*(ADCDataTab+nChannel+i*8)<minvalue)
minvalue=*(ADCDataTab+nChannel+i*8);
}
return ((averagevalue-maxvalue-minvalue)/18); //这样会耗时不可取 最好用 >>
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zyboy2000/archive/2009/09/24/4589395.aspx
【转帖】STM32开发板入门教程(六) - I2C--24Cxx 收藏
我们所用来示范的24Cxx系列是最常用的EEPROM芯片。
前面提到了一个地址码,
24Cxx的地址码是固定的,
8位如下:
1 0 1 0 A2 A1 A0 0
A2 A1 A0分别是它三个管脚的电平
24Cxx 理解起来有一个特别之处。
24Cxx 包括 01/02/04/08/16 四种,容量关系刚好和数字一样。1K 2K 4K 8K 16K
24C02 最为常见, 它的三个地址管脚A2 A1 A0都是可用的,
A2 A1 A0 有8中电平组合,也就是说,可以有8个 24C02 挂载同一个I2C总线上。
24C04呢, A0管脚就失效了,只有A2 和 A1 有用,四种组合,最多有4个24C04在总线上,
以此类推。24C16只能有一个在总线上。
这里就不好理解了,为什么要这样呢。
事实是一片 24C16 == 8片24C02 总线挂到一起。A2 A1 A0虽然起不到设置作用了,但你使用地址码还是会访问到特定的区域。
明白了吧。所以其实24C系列的代码是通用的。
地址码也是固定的。就是 0xA0 0xA2 0xA4 0xA6 0xA8 0xAA 0xAC 0xAE
好,我们以24C16为范例吧。
IO设置在I2C1上,无Remap,复用开漏输出。I2C 总线是挂4.7k电阻上拉到高电平的。
//-----------------------I2C--------------------------------------------
/* Configure I2C1 pins: SCL and SDA */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏输出
GPIO_Init(GPIOB, &GPIO_InitStruct);
I2C init函数:
void i2c_24c_init(I2C_TypeDef *I2Cx)
{
I2C_InitTypeDef I2C_InitStruct;
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; // I2C模式
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; // ACK 在通讯中常见,握手包,即发送到了一个数据,接收方回一句,我收到鸟。
I2C_InitStruct.I2C_ClockSpeed = I2C_Speed; // I2C 速度设置,一般是40KHZ,400KHZ是极限,一般到不了那么高
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; //快速模式下的选项,这里先不讲,100KHZ以上才有用
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //应答地址码长度,7位或者10位,24C是7位
// EEprom Block Select;
I2C_InitStruct.I2C_OwnAddress1 = I2C_Slave_Adress7; //第一个设备自身地址
I2C_Cmd(I2Cx,ENABLE); //开启I2C
I2C_Init(I2Cx,&I2C_InitStruct); //将刚刚的设置送进去
}
注意:现在的片子一般都不止一个I2C。所以用了上述模式,请详细看注释。
写一个字节进EEPROM:
参数解释:Byte待写的字节,WriteAddr预计写入的地址,ByteToWrite写多少给字节,EE24cBlockSelect选择EEPROM相应的区域(I2C地址),*I2Cx,I2C设备指针
void i2c_24c_byte_write(unsigned char Byte, unsigned char WriteAddr, unsigned int ByteToWrite, unsigned char EE24cBlockSelect,I2C_TypeDef *I2Cx)
{
// Start the I2C
I2C_GenerateSTART(I2Cx,ENABLE); //打开I2C,开始发送过程
//not recommanded, stupid way
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)); //设置主机模式
I2C_Send7bitAddress(I2Cx,EE24cBlockSelect,I2C_Direction_Transmitter); //发送片选,选择哪一片区域写。i2C地址区分
// when get ACK, means Set Success
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待这次选择过程完成
I2C_SendData(I2Cx, WriteAddr); //发送要写入的地址码
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待字节发送完成
I2C_SendData(I2Cx, Byte); //发送要写的字节
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待直到字节发送完成
I2C_GenerateSTOP(I2Cx, ENABLE); //发送过程结束。
}
读EEPROM函数类似,却稍微复杂。
参数说明:pBuffer接收I2C数据的缓冲区,Addr读的地址,NumToRead读多少个字节,ee24cblockselect读哪个区域,I2Cx i2c设备指针
void i2c_24c_buffer_read(unsigned char *pBuffer, unsigned char Addr,unsigned char NumToRead,unsigned char EE24cBlockSelect, I2C_TypeDef *I2Cx)
{
//open I2C
I2C_GenerateSTART(I2Cx, ENABLE); //开始发送
while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))); //设置自己为主机
I2C_Send7bitAddress(I2Cx,EE24cBlockSelect,I2C_Direction_Transmitter); //设置自己为发送
while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))); //等待主机发送模式设置成功
I2C_Cmd(I2Cx,ENABLE); //使能I2C
I2C_SendData(I2Cx, Addr); //发送地址码,即要读的地址
while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))); //等待主机发送过程完成
I2C_GenerateSTART(I2Cx, ENABLE); //I2C开始发送
while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))); //设置主机模式
I2C_Send7bitAddress(I2Cx,EE24cBlockSelect,I2C_Direction_Receiver); //设置从机地址,并设置主机为接收模式
while(!(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))); //确认该过程完成
while(NumToRead)
{
if(NumToRead==1)
{
I2C_AcknowledgeConfig(I2Cx, DISABLE); //关闭I2C的应答功能
I2C_GenerateSTOP(I2Cx, ENABLE); //发送结束信息
}
if((I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED))) //如果接收到信息了
{
*pBuffer = I2C_ReceiveData(I2Cx); //把接收到的数据 填进缓冲区当中
pBuffer++;
NumToRead--;
}
}
I2C_AcknowledgeConfig(I2Cx, ENABLE); //开启主机I2C的应答功能
}
在写i2C和读i2C之间要插入下面函数等待,否则会有问题
I2C_EE_WaitEepromStandbyState();
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zyboy2000/archive/2009/09/11/4542049.aspx
STM32笔记(三)ADC、DMA、USART的综合练习 收藏
/******************************************************************************
* 本文件实现ADC模块的基本功能
* 设置ADC1的常规转换序列包含CH10和CH16(片内温度传感器)
* 设置了连续转换模式,并使用DMA传输
* AD转换值被放在了AD_Value[2]数组内,[0]保存CH0结果,[1]保存CH16结果
* GetVolt函数计算[0]的值对应的电压值(放大100倍,保留2位小数)
* GetTemp函数计算[1]的值对应的温度值,计算公式在相应函数内有说明
*******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_lib.h"
#include "stdio.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_Address ((u32)0x4001244C)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
vu16 AD_Value[2];
vu16 i="0";
s16 Temp;
u16 Volt;
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART1_Configuration(void);
void ADC1_Configuration(void);
void DMA_Configuration(void);
int fputc(int ch, FILE *f);
void Delay(void);
u16 GetTemp(u16 advalue);
u16 GetVolt(u16 advalue);
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
USART1_Configuration();
DMA_Configuration();
ADC1_Configuration();
//启动第一次AD转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
//因为已经配置好了DMA,接下来AD自动连续转换,结果自动保存在AD_Value处
while (1)
{
Delay();
Temp = GetTemp(AD_Value[1]);
Volt = GetVolt(AD_Value[0]);
USART_SendData(USART1, 0x0c); //清屏
//注意,USART_SendData函数不检查是否发送完成
//等待发送完成
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
printf("电压:%d.%d\t温度:%d.%d℃\r\n", \
Volt/100, Volt%100, Temp/100, Temp%100);
}
}
/*******************************************************************************
* Function Name : 重定义系统putchar函数int fputc(int ch, FILE *f)
* Description : 串口发一个字节
* Input : int ch, FILE *f
* Output :
* Return : int ch
*******************************************************************************/
int fputc(int ch, FILE *f)
{
//USART_SendData(USART1, (u8) ch);
USART1->DR = (u8) ch;
/* Loop until the end of transmission */
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{
}
return ch;
}
/*******************************************************************************
* Function Name : Delay
* Description : 延时函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Delay(void)
{
u32 i;
for(i=0;i<0x4f0000;i++);
return;
}
/*******************************************************************************
* Function Name : GetTemp
* Description : 根据ADC结果计算温度
* Input : u16 advalue
* Output :
* Return : u16 temp
*******************************************************************************/
u16 GetTemp(u16 advalue)
{
u32 Vtemp_sensor;
s32 Current_Temp;
// ADC转换结束以后,读取ADC_DR寄存器中的结果,转换温度值计算公式如下:
// V25 - VSENSE
// T(℃) = ------------ + 25
// Avg_Slope
// V25: 温度传感器在25℃时 的输出电压,典型值1.43 V。
// VSENSE:温度传感器的当前输出电压,与ADC_DR 寄存器中的结果ADC_ConvertedValue之间的转换关系为:
// ADC_ConvertedValue * Vdd
// VSENSE = --------------------------
// Vdd_convert_value(0xFFF)
// Avg_Slope:温度传感器输出电压和温度的关联参数,典型值4.3 mV/℃。
Vtemp_sensor = advalue * 330 / 4096;
Current_Temp = (s32)(143 - Vtemp_sensor)*1000/34 + 2500;
return (s16)Current_Temp;
}
/*******************************************************************************
* Function Name : GetVolt
* Description : 根据ADC结果计算电压
* Input : u16 advalue
* Output :
* Return : u16 temp
*******************************************************************************/
u16 GetVolt(u16 advalue)
{
return (u16)(advalue * 330 / 4096);
}
/*******************************************************************************
* Function Name : RCC_Configuration
* Description : 系统时钟设置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;
//使能外部晶振
RCC_HSEConfig(RCC_HSE_ON);
//等待外部晶振稳定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
//如果外部晶振启动成功,则进行下一步操作
if(HSEStartUpStatus==SUCCESS)
{
//设置HCLK(AHB时钟)=SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//PCLK1(APB1) = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
//PCLK2(APB2) = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//设置ADC时钟频率
RCC_ADCCLKConfig(RCC_PCLK2_Div2);
//FLASH时序控制
//推荐值:SYSCLK = 0~24MHz Latency="0"
// SYSCLK = 24~48MHz Latency="1"
// SYSCLK = 48~72MHz Latency="2"
FLASH_SetLatency(FLASH_Latency_2);
//开启FLASH预取指功能
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//启动PLL
RCC_PLLCmd(ENABLE);
//等待PLL稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
//系统时钟SYSCLK来自PLL输出
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//切换时钟后等待系统时钟稳定
while(RCC_GetSYSCLKSource()!=0x08);
}
//下面是给各模块开启时钟
//启动GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\
ENABLE);
//启动AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//启动USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//启动DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//启动ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
}
/*******************************************************************************
* Function Name : GPIO_Configuration
* Description : GPIO设置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//PC口4567脚设置GPIO输出,推挽 2M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//KEY2 KEY3 JOYKEY
//位于PD口的3 4 11-15脚,使能设置为输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11 | GPIO_Pin_12 |\
GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//USART1_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//ADC_CH10--> PC0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/*******************************************************************************
* Function Name : NVIC_Configuration
* Description : NVIC设置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
#ifdef VECT_TAB_RAM
// Set the Vector Table base location at 0x20000000
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else /* VECT_TAB_FLASH */
// Set the Vector Table base location at 0x08000000
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
//设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//串口中断打开
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*******************************************************************************
* Function Name : USART1_Configuration
* Description : NUSART1设置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void USART1_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 19200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
/*******************************************************************************
* Function Name : ADC1_Configuration
* Description : ADC1设置(包括ADC模块配置和自校准)
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void ADC1_Configuration(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换开启
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2; //设置转换序列长度为2
ADC_Init(ADC1, &ADC_InitStructure);
//ADC内置温度传感器使能(要使用片内温度传感器,切忌要开启它)
ADC_TempSensorVrefintCmd(ENABLE);
//常规转换序列1:通道10
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
//常规转换序列2:通道16(内部温度传感器),采样时间>2.2us,(239cycles)
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5);
// Enable ADC1
ADC_Cmd(ADC1, ENABLE);
// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
ADC_DMACmd(ADC1, ENABLE);
// 下面是ADC自动校准,开机后需执行一次,保证精度
// Enable ADC1 reset calibaration register
ADC_ResetCalibration(ADC1);
// Check the end of ADC1 reset calibration register
while(ADC_GetResetCalibrationStatus(ADC1));
// Start ADC1 calibaration
ADC_StartCalibration(ADC1);
// Check the end of ADC1 calibration
while(ADC_GetCalibrationStatus(ADC1));
// ADC自动校准结束---------------
}
/*******************************************************************************
* Function Name : DMA_Configuration
* Description : DMA设置:从ADC模块自动读转换结果至内存
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//BufferSize=2,因为ADC转换序列有2个通道
//如此设置,使序列1结果放在AD_Value[0],序列2结果放在AD_Value[1]
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
//循环模式开启,Buffer写满后,自动回到初始地址开始传输
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
//配置完成后,启动DMA通道
DMA_Cmd(DMA1_Channel1, ENABLE);
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zyboy2000/archive/2009/08/24/4480497.aspx
STM32开发板入门教程 - 内部温度传感器 收藏
废话少说 先看看他的参数
1. STM32内部温度传感器与ADC的通道16相连,与ADC配合使用实现温度测量;
2. 测量范围–40~125℃,精度±1.5℃。
3. 温度传感器产生一个随温度线性变化的电压,转换范围在2V < VDDA < 3.6V之间。
转换公式如下图所示:
呵呵 其实 写代码的时候 公式直接简化就得啦 如果测量要求不怎么高的话 呵呵(其实高也高不了 呵呵)
我们都喜欢简单 简单明了 嘿嘿
简化的公式: vu16 Temperature= (1.42 - ADC_Value*3.3/4096)*1000/4.35 + 25;
呵呵 重新说一下 过程:
1. 初始化ADC 初始化DMA (大家可以参考马七的ADC教程 点击这里)
2. ADC_TempSensorVrefintCmd(ENABLE); 这个要开启哦 使能温度传感器和内部参考电压通道
3. 简单的数字滤波一下检测到的ADC的值
4. 按照刚才列出的公式计算 就OK啦 呵呵
第二步是做什么的呢? 看这个图就晓得啦
贴一下初始化的函数
/*******************************************************************************
* Function Name : ADC_Configuration
* Description : ADC_Configuration
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void ADC_Configuration(void)
{
/* DMA1 channel1 configuration ----------------------------------------------*/
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADCConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel14 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_55Cycles5);
/* Enable the temperature sensor and vref internal channel */
ADC_TempSensorVrefintCmd(ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
这个是抄袭马七的均值数字滤波函数 呵呵
/*******************************************************************************
* Function Name : ADC_Filter
* Description : ADC_Filter
* Input : None
* Output : None
* Return : ADC Converted Value
*******************************************************************************/
u16 ADC_Filter(void)
{
u16 result="0";
u8 i;
for(i=16;i>0;i--)
{
Delay_Ms(1);
result += ADCConvertedValue;
}
return result/16;
}
转换结果 往串口发送显示 (写的很烂哈)
ADC_Value = ADC_filter();
vu16 Temperature= (1.42 - ADC_Value*3.3/4096)*1000/4.35 + 25;
ADC_Value = Temperature;
a = ADC_Value/1000;
b = (ADC_Value - a*1000)/100;
c = (ADC_Value - a*1000 - b*100)/10;
d = ADC_Value - a*1000 - b*100 - c*10;
&
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。