嵌入式ADC、DAC、I2C实验

ADC实验

实验时间:2020.12.9
实验环境:NEWLab;M3核心模块

实验内容1:ADC基础实验

实验目的

  1. 了解和熟悉CortexM3的ADC基本特性
  2. 掌握STM32ADC的库函数编程方法
  3. 掌握GPIO管脚复用功能的使用方法

实验要求

利用ADC1的通道0,即PA0,采集输入电压

实验步骤

  1. 搭建LED电路:从PA0引出一条杜邦线
  2. 配置工程
    在这里插入图片描述
  3. 源程序
    在这里插入图片描述
#include "stm32f10x.h"
#include "stdio.h" 

void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void ADC_Configuration(void);  //ADC配置

void delay_nms(u16 time);
int fputc(int ch, FILE *f);

int main(void)
{  
	u16 adcx;
	float result;
	
   //SystemInit(); 
	 RCC_Configuration();	
   GPIO_Configuration();
   USART_Configuration();
   ADC_Configuration();
   while(1)
   {
		 	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
		 //设置规则通道,设置它们的转化顺序和采样时间
			//总转换时间T_conv=采样时间+12.5cycles
			// T_conv=239.5+12.5=252cycles;
			// T_conv=252/12MHz=21us
			//Start ADC1 Software Conversion */ 
			ADC_SoftwareStartConvCmd(ADC1, ENABLE);

			/* Test if the ADC1 EOC flag is set or not */
			while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
			adcx = ADC_GetConversionValue(ADC1);
			result=(float)adcx*(3.3/4095);//获取相应的电压值
			printf("电压为:%0.3fV\r\n",result);
			delay_nms(1000);
		  GPIOA->ODR^=(1<<8);//蜂鸣器嗒一下
   }
}

void RCC_Configuration(void)
{
	//SystemInit();
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1的时钟, USART1挂接到APB2上。其他USART2-5挂接到APB1上
}

 void GPIO_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;//声明GPIO初始化结构变量。
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //配置管脚8
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //IO口配置为推挽输出口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //工作频率50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA8口

} 
 
void ADC_Configuration(void)
{
	ADC_InitTypeDef ADC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	/* Enable ADC1 and GPIOA clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);//开启ADC1的时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHZ 

	/* Configure PA.0 (ADC Channel) as analog input -------------------------*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOA, &GPIO_InitStructure);	  
      /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//选择ADC工作在独立模式或者双ADC模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; //权责工作在扫描模式(多通道)还是单次(单通道)模式
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//选择ADC工作在连续还是单次模式。
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//定义使用外部触发来启动规则通道的ADC
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//设置数据左对齐还是右对齐
  ADC_InitStructure.ADC_NbrOfChannel = 1;//设置进行规则转换的ADC通道数目
  ADC_Init(ADC1, &ADC_InitStructure);
  ADC_Cmd(ADC1, ENABLE);//使能ADC1
  ADC_ResetCalibration(ADC1); // 复位ADC1校准寄存器
  while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准复位结束
  ADC_StartCalibration(ADC1);//开始校准
  while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
 
void USART_Configuration(void)
{
	USART_InitTypeDef USART_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;//声明GPIO初始化结构变量。
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //配置管脚PA10/USART1_RX 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮置输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA10
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //配置管脚PA9/USART1_TX
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //IO口配置为复用输出口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9	
	
	USART_InitStructure.USART_BaudRate = 115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//USART_WordLength_8b; //8 位数据	//USART_WordLength_9b; //9 位数据
	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_Tx;//发送使能
	//USART_Mode_Rx;//接收使能	
	USART_Init(USART1, &USART_InitStructure);//初始化串口
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启接收中断,接收寄存器不空(RXNE=1)时产生中断  
	USART_ITConfig(USART1, USART_IT_TXE, ENABLE); // 开启发送中断,发送寄存器空(TXE=1)时能产生中断
  USART_Cmd(USART1, ENABLE); //启动USART
}


int fputc(int ch,FILE *f)
{
	if(ch=='\n')
	{
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
			USART_SendData(USART1,'\r');
	}
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
		USART_SendData(USART1,ch);        
    return ch;
}
void delay_nms(u16 time)//延时子程序
{  u16 i=0;  
   while(time--)
   {  i=12000;  //自己定义
      while(i--) ; 
   }
}


 

  1. 编译、下载、运行程序,打开串口调试助手,首先,把PA0接到3.3V上,如图所示。此时,串口调试助手显示所测电压为3.299V~3.300V。

在这里插入图片描述

  1. 把PA0接到GND上,如所示图145所示。此时,串口调试助手显示所测电压为0.00V。
    在这里插入图片描述
  2. 把PA0接到“温度/光照度传感模块”上的J10口,如下图所示,调整蓝色的电位器,即可观察到电压变化。该电位器的电路图如所示。
    在这里插入图片描述

实验内容2:ADC内部温度传感

实验目的

  1. 了解和熟悉CortexM3的ADC基本特性
  2. 掌握STM32ADC的库函数编程方法
  3. 掌握GPIO管脚复用功能的使用方法

实验要求

利用ADC1的通道0,即PA0,采集输入电压。

实验步骤

  1. 搭建LED电路:
    在这里插入图片描述
    在这里插入图片描述
  2. 工程配置
    在这里插入图片描述
  3. 源程序
    在这里插入图片描述
#include "stm32f10x.h"
#include "stdio.h" 

void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void ADC_Configuration(void);  //ADC配置

void delay_nms(u16 time);
int fputc(int ch, FILE *f);

int main(void)
{ 
 
	u16 adcx;
	float Vsense,T;
	
   SystemInit(); 
	 RCC_Configuration();	
   GPIO_Configuration();
   USART_Configuration();
   ADC_Configuration();
   while(1)
   {
		 	ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);
		 //设置规则通道,设置它们的转化顺序和采样时间
			//总转换时间T_conv=采样时间+12.5cycles
			// T_conv=239.5+12.5=252cycles;
			// T_conv=252/12MHz=21us
			//Start ADC1 Software Conversion */ 
			ADC_SoftwareStartConvCmd(ADC1, ENABLE);

			/* Test if the ADC1 EOC flag is set or not */
			while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
			  adcx = ADC_GetConversionValue(ADC1);
			Vsense=(float)adcx*(3.3/4095);//获取相应的电压值
			T=(1.43-Vsense)*1000/4.3+25;
			printf("温度为:%0.3f℃\r\n",T);
			delay_nms(1000);
			GPIOA->ODR^=(1<<8);//LED灯闪烁
   }
}

void RCC_Configuration(void)
{
	SystemInit();
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1的时钟, USART1挂接到APB2上。其他USART2-5挂接到APB1上
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
}

 void GPIO_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;//声明GPIO初始化结构变量。
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //配置管脚8
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //IO口配置为推挽输出口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //工作频率50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA8口

} 
 
void ADC_Configuration(void)
{
	ADC_InitTypeDef ADC_InitStructure;
//	GPIO_InitTypeDef GPIO_InitStructure;
	/* Enable ADC1 and GPIOA clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);//开启ADC1的时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12MHZ 
	
	ADC_TempSensorVrefintCmd(ENABLE); //开启内部温度传感器
  /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//选择ADC工作在独立模式或者双ADC模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; //权责工作在扫描模式(多通道)还是单次(单通道)模式
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//选择ADC工作在连续还是单次模式。
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//定义使用外部触发来启动规则通道的ADC
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//设置数据左对齐还是右对齐
  ADC_InitStructure.ADC_NbrOfChannel = 1;//设置进行规则转换的ADC通道数目
  ADC_Init(ADC1, &ADC_InitStructure);
  ADC_Cmd(ADC1, ENABLE);//使能ADC1
  /* Enable ADC1 reset calibration register */   
  ADC_ResetCalibration(ADC1); // 复位ADC1校准寄存器
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准复位结束
  /* Start ADC1 calibration */
  ADC_StartCalibration(ADC1);//开始校准
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}

 

 
void USART_Configuration(void)
{
	USART_InitTypeDef USART_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;//声明GPIO初始化结构变量。
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //配置管脚PA10/USART1_RX 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮置输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA10
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //配置管脚PA9/USART1_TX
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //IO口配置为复用输出口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9	
	
	USART_InitStructure.USART_BaudRate = 115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//USART_WordLength_8b; //8 位数据	//USART_WordLength_9b; //9 位数据
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	//USART_StopBits_1 ;//在帧结尾传输 1 个停止位
	//USART_StopBits_0.5;//在帧结尾传输 0.5 个停止位
	//USART_StopBits_2 ;//在帧结尾传输 2 个停止位
	//USART_StopBits_1.5;//在帧结尾传输 1.5 个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;
	//USART_Parity_No ;//奇偶失能
	//USART_Parity_Even;//偶模式
	//USART_Parity_Odd ;//奇模式
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	//USART_HardwareFlowControl_None; //硬件流控制失能
	//USART_HardwareFlowControl_RTS; //发送请求 RTS使能
	//USART_HardwareFlowControl_CTS; //清除发送 CTS使能
	//USART_HardwareFlowControl_RTS_CTS;//RTS和 CTS使能
	USART_InitStructure.USART_Mode = USART_Mode_Tx;
	//USART_Mode_Tx;//发送使能
	//USART_Mode_Rx;//接收使能	
	USART_Init(USART1, &USART_InitStructure);//初始化串口
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启接收中断,接收寄存器不空(RXNE=1)时产生中断  
	USART_ITConfig(USART1, USART_IT_TXE, ENABLE); // 开启发送中断,发送寄存器空(TXE=1)时能产生中断
	/*串口的发送中断有两个,分别是:
		l发送数据寄存器空中断(TXE)
		l发送完成中断(TC)*/
  USART_Cmd(USART1, ENABLE); //启动USART
}


int fputc(int ch,FILE *f)
{
	if(ch=='\n')
	{
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
			USART_SendData(USART1,'\r');
	}
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
		USART_SendData(USART1,ch);        
    return ch;
}
void delay_nms(u16 time)//延时子程序
{  u16 i=0;  
   while(time--)
   {  i=12000;  //自己定义
      while(i--) ; 
   }
}


 

  1. 实验结果
    在这里插入图片描述

思考与体会

我们通过使用ADC库函数设置其工作模式及通道,设置其通道规则实现采样,通过采样时间来确保其转换结果的稳定性。在实验中,我们从使用的PA0管脚引出一条杜邦线,可看做万用表的红表笔。

DAC实验

实验时间:2020.12.9
实验环境:NEWLab;M3核心模块

实验内容:DAC基础实验

实验目的

  1. 了解和熟悉CortexM3的DAC基本特性
  2. 掌握STM32DAC的库函数编程方法
  3. 掌握GPIO管脚复用功能的使用方法

实验要求

设置任何一个0-3.3V的电压数字,通过DAC1即PA4的输出管脚给出所设定的模拟电压。用万用表或者示波器观察输出电压情况。

实验步骤

  1. 搭建LED电路
  2. 配置工程
    在这里插入图片描述
  3. 源程序
    在这里插入图片描述
//步骤1,拷贝基本的USART实验工程文件夹
//步骤2,添加DAC相关的函数。
//步骤3,修改主程序,显示DAC数值。
//利用串口调试助手,观察输出提示。
//利用万用表,测试PA04管脚的输出电压。
//通过USART1发送数据,PC机上运行串口调试助手软件接收字符
//如果串口没有显示正确的输出,请检查Target/Code Generation中的Use MicroLIB项是否被选择。

#include "stm32f10x.h"
#include <stdio.h>

void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void DAC1_Init(void);
void DAC1_OutVoltage(float Voltage);
int fputc(int ch, FILE *f);/*重定向,修改一下选中Use MicroLIB ,
														 需要在 Target/Code Generation选中Use MicroLIB 
														 才能使用printf*/
void delay_nms(u16 time);//延时子程序

int main(void)
{
	float voltage=2;
	RCC_Configuration();
	GPIO_Configuration();
	USART_Configuration();
  DAC1_Init();
	
	USART_ClearFlag(USART1,USART_FLAG_TC);//清发送结束位,在发送第1位数据之前需要清除USART_FLAG_TC标志,
	//否则容易造成第一个数据发送不出去
	
	DAC1_OutVoltage(voltage);
	while(1)
	{
		GPIOA->ODR^=(1<<8); //Toggle LED
		delay_nms(1000);
		
		if (voltage<3.3)
		{voltage+=0.1;}
		else
		{voltage=0;}
		//voltage=3.3;
		DAC1_OutVoltage(voltage);
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
		printf("DAC输入的数字为:%d;",(uint16_t)(voltage*4095/3.3));
		printf("DAC输出电压为:%.3f\n",voltage);
	}
}

void RCC_Configuration(void)
{
	SystemInit();
  
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA的时钟	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1的时钟, USART1挂接到APB2上。其他USART2-5挂接到APB1上
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
}
void GPIO_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;//声明GPIO初始化结构变量。
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //配置管脚PA10/USART1_RX 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮置输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA10
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //配置管脚PA9/USART1_TX
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //IO口配置为复用输出口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9	
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //配置管脚8
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //IO口配置为推挽输出口
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //工作频率50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA8口

}
 
void USART_Configuration(void)
{
	USART_InitTypeDef USART_InitStructure;
	
	USART_InitStructure.USART_BaudRate = 115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//USART_WordLength_8b; //8 位数据	//USART_WordLength_9b; //9 位数据
	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_Tx;//发送使能
	//USART_Mode_Rx;//接收使能	
	USART_Init(USART1, &USART_InitStructure);//初始化串口
  USART_Cmd(USART1, ENABLE); //启动USART
}


int fputc(int ch,FILE *f)
{
	if(ch=='\n')
	{
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
			USART_SendData(USART1,'\r');
	}
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
		USART_SendData(USART1,ch);        
    return ch;
}

void delay_nms(u16 time)//延时子程序
{  u16 i=0;  
   while(time--)
   {  i=12000;  //自己定义
      while(i--) ; 
   }
}

void DAC1_Init(void)
{
  
	GPIO_InitTypeDef GPIO_InitStructure;
	DAC_InitTypeDef DAC_InitType;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );	  //使能PORTA通道时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );	  //使能DAC通道时钟 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; 		 //模拟输入(STM32没有模拟输出模式,这里只好配置为模拟输入)
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
					
	DAC_InitType.DAC_Trigger=DAC_Trigger_None;	//不使用触发功能 TEN1=0
	DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
	DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
	DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;	//DAC1输出缓存关闭 BOFF1=1
  DAC_Init(DAC_Channel_1,&DAC_InitType);	 //初始化DAC通道1

	DAC_Cmd(DAC_Channel_1, ENABLE);  //使能DAC1
  
  DAC_SetChannel1Data(DAC_Align_12b_R, 0);  //12位右对齐数据格式设置DAC值,默认输出0V

}

//设置通道1输出电压
// Voltage----电压值,取值范围为0~3.3V
void DAC1_OutVoltage(float Voltage)
{
	uint16_t data;
	data=(uint16_t)(Voltage*4095/3.3);//换算为12位整数值
	DAC_SetChannel1Data(DAC_Align_12b_R,data);//12位右对齐数据格式设置DAC值
	DAC_SoftwareTriggerCmd(DAC_Channel_1,ENABLE); //启动转换
}



  1. 串口调试助手显示DAC输入数字和对应的DAC期望输出电压。用万用表测量PA4,其数值应该与串口调试助手输出一致。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思考与体会

串口中输出的是我们预期电压的理论值,是通过计算出来的,而为了验证我们可以使用两种办法:
1.直接使用万用表直观测量电压
2.与ADC模块结合,由于本次实验是DAC,即将数字转化为模拟,而正好可以通过上一个ADC实验实现模拟到数字的转换
本次实验使用的是第一种方式,直观对比,在电压较小时比较精确,随着电压的增加,会出现较小的误差

IIC串行RTC

实验时间:2020.12.9
实验环境:NEWLab;M3核心模块

实验内容: IIC串行RTC

实验目的

利用功能扩展模块中的时钟单元制作一个电子钟,能够通过M3核心模块将RTC芯片中的时间数据通过IIC总线读取出来,在LCD12864上显示当前时间,并能够通过串口助手下发时间调整命令对RTC芯片的时钟值进行调整。

实验步骤

  1. 准备源文件
    在这里插入图片描述

  2. 添加源文件
    在这里插入图片描述

  3. 配置工程
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  4. 配置IIC
    在这里插入图片描述

  5. 源程序

/* RTC时钟实验
功能描述:STM32F103通过IIC协议获取RTC芯片PCF8563的当前时钟,然后通过数码管显示出来;
利用串口调试助手,通过下发命令“set+YYMMDDHHMMSS”实现对PCF8563时间的调整。
*/

#include "stm32f10x.h"
#include <stdio.h>
#include <string.h>
#include "delay.h"
#include "hal_usart1.h"
#include "rtc_pcf8563.h"
#include "lcd12864.h"
#include "Font.h"

int main(void)
{	
	char	*p;
	uint8_t	i;
	uint8_t time_Hex[7];//以BCD码的形式存放 年月日时分秒。其中年占用time_Hex[0] time_Hex[1]
	//由于BCD码是每4位为一组,而计算机默认二进制数据格式,因此,BCD码表示的数字体现为一个二进制数字
	//例如,十进制的20,翻译为二进制就是0010 0000=32,
	//例如,十进制的18,翻译为二进制就是0001 1000=24
	uint8_t time_Dec[7];//以十进制码的形式存放 年月日时分秒。
	uint8_t time_set[7]={32,24,7,48,0,35,0};//时间预设值,BCD码,20 18 07 30 00 23 00
	uint8_t timeset_rx[USART1_REC_LEN];//时间设置数据缓存
	
	
	delay_init();//初始化延时函数
	delay_ms(100);//延时100ms待系统稳定	
	USART1_Init(115200);//初始化串口

	
  printf("RTC时钟PCF8563实验\r\n");

	
	pcf8563_init();//初始化PCF8563
	delay_ms(100);	
	if (!pcf8563_init())//检测初始化是否成功
	{
		printf("RTC时钟故障, 请检查硬件连接! \r\n");
		printf("排除故障后, 请重新上电! \r\n");
		while(1);
	}
	
	pcf8563_get_time(time_Hex);//获取PCF8563的时间,依次打印出:年月日时分秒,
	//其中年占用time[0] time[1]。
	printf("年(高位): %d\r\n",*time_Hex);
	printf("年(低位): %d\r\n",*(time_Hex+1));
	printf("月: %d\r\n",*(time_Hex+2));
	printf("日: %d\r\n",*(time_Hex+3));
	printf("时: %d\r\n",*(time_Hex+4));
	printf("分: %d\r\n",*(time_Hex+5));
	printf("秒: %d\r\n",*(time_Hex+6));	
	
	
	printf("获取时间数字的二进制编码形式为:\r\n");
	for(i=0; i < sizeof(time_Hex); i++){
		printf("%d ", time_Hex[i]);
	}
  printf("\r\n");
	 printf("上述时间数字的十进制形式为:\r\n");
	for(i=0; i < sizeof(time_Hex); i++){
		time_Dec[i]=(time_Hex[i]>>4)*10+(time_Hex[i]&0x0F);
	}
	printf("%d%d年%d月%d日%d-%d-%d", time_Dec[0], time_Dec[1], time_Dec[2], 
	time_Dec[3], time_Dec[4], time_Dec[5], time_Dec[6]);	
	//pcf8563_wirte_time(time_set); %这里设置当前时间。
	
	printf("\r\n");
  printf("当前时间显示在LCD 12864上。\r\n");
	printf("如果要修改时间,请在串口调试助手中输入以\"set\"命令为首的待校正时间。\r\n");
	printf("命令格式为:setYYMMDDHHMMSS\r\n");
	printf("例如:set20180803171501,可以将时间设置为2018年8月3日17时15分01秒。\r\n");
	LCD_Init();//初始化显示屏
	LCD_Clr(); //清屏

	LCD_WriteChineseString(0,0,(unsigned char *)hanzi,6);			
	LCD_WriteChineseString(2,8,(unsigned char *)hanzi3,4);
	memset(timeset_rx,USART1_REC_LEN,0);
	memset(USART1_RX_BUF,USART1_REC_LEN,0);
	while(1)
	{
		pcf8563_get_time(time_Hex);//获取时钟
		printf("%02X%02X年%02X月%02X日 %02X:%02X:%02X\r\n", time_Hex[0], time_Hex[1], time_Hex[2],
		time_Hex[3],time_Hex[4],time_Hex[5],time_Hex[6]);	
		delay_ms(800);
		//YYYY年MM月DD日
		LCD_WriteEnglish(4,0,(time_Hex[0]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(4,8,(time_Hex[0]&0x0F)+'0');//取低4位
		LCD_WriteEnglish(4,2*8,(time_Hex[1]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(4,3*8,(time_Hex[1]&0x0F)+'0');//取低4位
		LCD_WriteChinese(4,4*8,(unsigned char *)hanzi3[8]);//年
		LCD_WriteEnglish(4,6*8,(time_Hex[2]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(4,7*8,(time_Hex[2]&0x0F)+'0');//取低4位
		LCD_WriteChinese(4,8*8,(unsigned char *)hanzi3[10]);//月
		LCD_WriteEnglish(4,10*8,(time_Hex[3]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(4,11*8,(time_Hex[3]&0x0F)+'0');//取低4位
		LCD_WriteChinese(4,12*8,(unsigned char *)hanzi3[12]);//日
		
		LCD_WriteEnglish(6,2*8,(time_Hex[4]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(6,3*8,(time_Hex[4]&0x0F)+'0');//取低4位
		LCD_WriteChinese(6,4*8,(unsigned char *)hanzi3[14]);//时		
		LCD_WriteEnglish(6,6*8,(time_Hex[5]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(6,7*8,(time_Hex[5]&0x0F)+'0');//取低4位
		LCD_WriteChinese(6,8*8,(unsigned char *)hanzi3[16]);//分
		LCD_WriteEnglish(6,10*8,(time_Hex[6]>>4)+'0');//取高4位,将BCD码转化为ASCII码
		LCD_WriteEnglish(6,11*8,(time_Hex[6]&0x0F)+'0');//取低4位
		LCD_WriteChinese(6,12*8,(unsigned char *)hanzi3[18]);//秒			
		

		if(Rx_Flag==1) //如果接收到一组以换行结束的数据,则将接收到的数据再发送出去。
	 {            //判断是否收到一帧有效数据
		 Rx_Flag=0;
		 printf("USART1_RX_BUF=%d\r\n",USART1_RX_COUNT);
		 printf("timeset_rx=");
		for (i=0;i<USART1_RX_COUNT;i++)//将USART1接收缓存的数据转存到timeset_rx
		{
			timeset_rx[i]=USART1_RX_BUF[i];
			printf("%c",timeset_rx[i]);
		 }
		 memset(USART1_RX_BUF,USART1_REC_LEN,0);			 
		 USART1_RX_COUNT=0;
		 printf("%X%X-%X-%X %X:%X:%X\r\n", time_Hex[0], time_Hex[1], time_Hex[2],
		 time_Hex[3],time_Hex[4],time_Hex[5],time_Hex[6]);
     p=strstr(timeset_rx,"set");//查找set
		 if (p==NULL)
		 {
			 printf("[set] not found!");
		 }
		 else
		 {
			 memset(time_Dec,7,0);
			 memset(time_Hex,7,0);				 
			 printf("time_Dec=");
			 for (i=0;i<7;i++)
			 {
				 time_Dec[i]=10*(*(p+3+2*i)-'0')+(*(p+4+2*i)-'0');//取出set后面的7个数据,按照十进制的方式进行存储
				 time_Hex[i]=((*(p+3+2*i)-'0')<<4)+(*(p+4+2*i)-'0');//将BCD码表示的数字转化为二进制数字。
				 printf("%02d",time_Dec[i]);
			 }
			 printf("\r\n");
			 
			 printf("time_Hex=");
			 for (i=0;i<7;i++)
			 {
				 printf("%02d ",time_Hex[i]);
			 }	
			 printf("\r\n");
			 printf("time_Dec=");
			 for (i=0;i<7;i++)
			 {
				 printf("%02d ",time_Dec[i]);
			 }
       printf("\r\n");			 
       if(pcf8563_check_validity(time_Hex))			 
			 {
				 printf("设置time_Dec为当前时刻。。。\n\r");
				 pcf8563_wirte_time(time_Hex); //这里设置当前时间。
				 printf("时间已设置。\n\r");
				 }
			 else
			 { 
				 printf("时间未设置,请检查命令格式是否正确!!\n\r");
			 }
			 memset(timeset_rx,USART1_REC_LEN,0);
		 } 
		} 
		delay_ms(100);
	}

}




IIC配置:
IIC初始化

static int32_t I2C_Initialize (ARM_I2C_SignalEvent_t cb_event, I2C_RESOURCES *i2c) {

  if (i2c->info->flags & I2C_INIT) { return ARM_DRIVER_OK; }

  GPIO_AFConfigure(i2c->io.remap);

  /* Configure SCL Pin */
  GPIO_PortClock   (i2c->io.scl_port, true);
  GPIO_PinConfigure(i2c->io.scl_port, i2c->io.scl_pin, GPIO_AF_OPENDRAIN,
                                                       GPIO_MODE_OUT50MHZ);
  /* Configure SDA Pin */
  GPIO_PortClock   (i2c->io.sda_port, true);
  GPIO_PinConfigure(i2c->io.sda_port, i2c->io.sda_pin, GPIO_AF_OPENDRAIN,
                                                       GPIO_MODE_OUT50MHZ);

  /* Initialize DMA Channels */
  #if defined(USE_I2C1_DMA) || defined(USE_I2C2_DMA)
    if ((i2c->dma_rx != NULL) && (i2c->dma_tx != NULL)) {
      DMA_ChannelInitialize(i2c->dma_rx->dma_num, i2c->dma_rx->channel);
      DMA_ChannelInitialize(i2c->dma_tx->dma_num, i2c->dma_tx->channel);
    }
  #endif

  /* Reset Run-Time information structure */
  memset (i2c->info, 0x00, sizeof (I2C_INFO));

  i2c->info->cb_event = cb_event;
  i2c->info->flags    = I2C_INIT;

  return ARM_DRIVER_OK;
}

主传输:

static int32_t I2C_MasterTransmit (uint32_t       addr,
                                   const uint8_t *data,
                                   uint32_t       num,
                                   bool           xfer_pending,
                                   I2C_RESOURCES *i2c) {

  if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if ((addr & ~(ARM_I2C_ADDRESS_10BIT | ARM_I2C_ADDRESS_GC)) > 0x3FFU) {
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
  }

  if ((i2c->info->xfer.ctrl & XFER_CTRL_XPENDING) == 0U) {
    /* New transfer */
    while (i2c->reg->SR2 & I2C_SR2_BUSY) {
      ; /* Wait until bus released */
    }
  }

  i2c->info->status.busy             = 1U;
  i2c->info->status.mode             = 1U;
  i2c->info->status.direction        = 0U;
  i2c->info->status.bus_error        = 0U;
  i2c->info->status.arbitration_lost = 0U;

  i2c->info->xfer.num  = num;
  i2c->info->xfer.cnt  = 0U;
  i2c->info->xfer.data = (uint8_t *)data;
  i2c->info->xfer.addr = (uint16_t)(addr);
  i2c->info->xfer.ctrl = 0U;

  if (xfer_pending) {
    i2c->info->xfer.ctrl |= XFER_CTRL_XPENDING;
  }

  #if defined(USE_I2C1_DMA) || defined(USE_I2C2_DMA)
  if (i2c->dma_tx) {
    /* Configure and enable DMA channel */
    DMA_ChannelConfigure (i2c->dma_tx->reg,
                          (i2c->dma_rx->priority << DMA_PRIORITY_POS)|
                          DMA_MEMORY_DATA_8BIT                       |
                          DMA_PERIPHERAL_DATA_8BIT                   |
                          DMA_MEMORY_INCREMENT                       |
                          DMA_READ_MEMORY                            |
                          DMA_TRANSFER_COMPLETE_INTERRUPT,
                         (uint32_t)&(i2c->reg->DR),
                         (uint32_t)data,
                          num);
    DMA_ChannelEnable (i2c->dma_tx->reg);
  }
  #endif

  /* Generate start and enable event interrupts */
  i2c->reg->CR2 &= ~I2C_CR2_ITEVTEN;
  i2c->reg->CR1 |=  I2C_CR1_START;
  i2c->reg->CR2 |=  I2C_CR2_ITEVTEN;
  return ARM_DRIVER_OK;
}

主接收:

static int32_t I2C_MasterReceive (uint32_t       addr,
                                  uint8_t       *data,
                                  uint32_t       num,
                                  bool           xfer_pending,
                                  I2C_RESOURCES *i2c) {

  if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
  }
  if ((addr & ~(ARM_I2C_ADDRESS_10BIT | ARM_I2C_ADDRESS_GC)) > 0x3FFU) {
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
  }

  if ((i2c->info->xfer.ctrl & XFER_CTRL_XPENDING) == 0U) {
    /* New transfer */
    while (i2c->reg->SR2 & I2C_SR2_BUSY) {
      ; /* Wait until bus released */
    }
  }

  i2c->info->status.busy             = 1U;
  i2c->info->status.mode             = 1U;
  i2c->info->status.direction        = 1U;
  i2c->info->status.bus_error        = 0U;
  i2c->info->status.arbitration_lost = 0U;

  i2c->info->xfer.num  = num;
  i2c->info->xfer.cnt  = 0U;
  i2c->info->xfer.data = data;
  i2c->info->xfer.addr = (uint16_t)(addr);
  i2c->info->xfer.ctrl = 0U;

  if (xfer_pending) {
    i2c->info->xfer.ctrl |= XFER_CTRL_XPENDING;
  }

  /* Enable acknowledge generation */
  i2c->reg->CR1 |= I2C_CR1_ACK;

  #if defined(USE_I2C1_DMA) || defined(USE_I2C2_DMA)
  if (i2c->dma_rx) {
    /* Configure and enable DMA channel */
    DMA_ChannelConfigure (i2c->dma_rx->reg,
                          (i2c->dma_rx->priority << DMA_PRIORITY_POS)|
                          DMA_MEMORY_DATA_8BIT                       |
                          DMA_PERIPHERAL_DATA_8BIT                   |
                          DMA_MEMORY_INCREMENT                       |
                          DMA_PERIPHERAL_TO_MEMORY                   |
                          DMA_TRANSFER_COMPLETE_INTERRUPT,
                         (uint32_t)&(i2c->reg->DR),
                         (uint32_t)data,
                          num);

    DMA_ChannelEnable (i2c->dma_rx->reg);

    /* Permit generation of a NACK on the last received data */
    i2c->reg->CR2 |= I2C_CR2_LAST;
  }
  #endif

  /* Generate start and enable event interrupts */
  i2c->reg->CR2 &= ~I2C_CR2_ITEVTEN;
  i2c->reg->CR1 |=  I2C_CR1_START;
  i2c->reg->CR2 |=  I2C_CR2_ITEVTEN;

  return ARM_DRIVER_OK;
}

从传输:

static int32_t I2C_SlaveTransmit (const uint8_t *data, uint32_t num, I2C_RESOURCES *i2c) {

  if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
  }

  i2c->info->status.bus_error    = 0U;
  i2c->info->status.general_call = 0U;

  i2c->info->xfer.num  = num;
  i2c->info->xfer.cnt  = 0U;
  i2c->info->xfer.data = (uint8_t *)data;
  i2c->info->xfer.ctrl = 0U;

  #if defined(USE_I2C1_DMA) || defined(USE_I2C2_DMA)
  if (i2c->dma_tx) {
    /* Configure and enable DMA channel */
    DMA_ChannelConfigure (i2c->dma_tx->reg,
                          (i2c->dma_tx->priority << DMA_PRIORITY_POS)|
                          DMA_MEMORY_DATA_8BIT                       |
                          DMA_PERIPHERAL_DATA_8BIT                   |
                          DMA_MEMORY_INCREMENT                       |
                          DMA_READ_MEMORY                            |
                          DMA_TRANSFER_COMPLETE_INTERRUPT,
                         (uint32_t)&(i2c->reg->DR),
                         (uint32_t)data,
                          num);
    DMA_ChannelEnable (i2c->dma_tx->reg);
  }
  #endif

  /* Enable acknowledge */
  i2c->reg->CR1 |= I2C_CR1_ACK;

  /* Enable event interrupts */
  i2c->reg->CR2 |= I2C_CR2_ITEVTEN;
  return ARM_DRIVER_OK;
}

从接收:

static int32_t I2C_SlaveReceive (uint8_t *data, uint32_t num, I2C_RESOURCES *i2c) {

  if ((data == NULL) || (num == 0U)) {
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (i2c->info->status.busy) {
    return (ARM_DRIVER_ERROR_BUSY);
  }

  i2c->info->status.bus_error    = 0U;
  i2c->info->status.general_call = 0U;

  i2c->info->xfer.num  = num;
  i2c->info->xfer.cnt  = 0U;
  i2c->info->xfer.data = data;
  i2c->info->xfer.ctrl = 0U;

  /* Enable acknowledge generation */
  i2c->reg->CR2 |= I2C_CR2_LAST;

  #if defined(USE_I2C1_DMA) || defined(USE_I2C2_DMA)
  if (i2c->dma_rx) {
    /* Configure and enable DMA channel */
    DMA_ChannelConfigure (i2c->dma_rx->reg,
                          (i2c->dma_rx->priority << DMA_PRIORITY_POS)|
                          DMA_MEMORY_DATA_8BIT                       |
                          DMA_PERIPHERAL_DATA_8BIT                   |
                          DMA_MEMORY_INCREMENT                       |
                          DMA_PERIPHERAL_TO_MEMORY                   |
                          DMA_TRANSFER_COMPLETE_INTERRUPT,
                         (uint32_t)&(i2c->reg->DR),
                         (uint32_t)data,
                          num);

    DMA_ChannelEnable (i2c->dma_rx->reg);
  }
  #endif

  /* Enable acknowledge */
  i2c->reg->CR1 |= I2C_CR1_ACK;

  /* Enable event interrupts */
  i2c->reg->CR2 |= I2C_CR2_ITEVTEN;
  return ARM_DRIVER_OK;
}
  1. 连接路线
    在这里插入图片描述

在这里插入图片描述
7. 观察显示及串口
在这里插入图片描述
在这里插入图片描述

  1. 修改时间
    在这里插入图片描述
    在这里插入图片描述

思考与体会

对于这个实验,从原理上不太能理解,在老师解释后,着重研究了I2C的配置,但还是不太明白,于是上网百度进行了I2C的配置的一些学习:
IIC通信有3种类型的信号:开始信号,结束信号,和应答信号。

开始信号:SCL为高电平,SDA由高电平向低电平跳变,表示可以开始传输信号,进行通信了。

结束信号:SCL为高电平,SDA由低电平向高电平跳变,表示传输信号的时间已经过了。

应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。

CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,这就是开始信号。结束信号也一样,处理器不可能一直处于与其他IC通信的状态的。而应答信号,发送方把自己要发送的数据发送出去了,但不知道对方有没有收到,所以有些情况,需要等待接收方返回应答信号,告诉发送方我已经收到了,你可以继续发送下一条数据。

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值