文章目录
ADC实验
实验时间:2020.12.9
实验环境:NEWLab;M3核心模块
实验内容1:ADC基础实验
实验目的
- 了解和熟悉CortexM3的ADC基本特性
- 掌握STM32ADC的库函数编程方法
- 掌握GPIO管脚复用功能的使用方法
实验要求
利用ADC1的通道0,即PA0,采集输入电压
实验步骤
- 搭建LED电路:从PA0引出一条杜邦线
- 配置工程
- 源程序
#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--) ;
}
}
- 编译、下载、运行程序,打开串口调试助手,首先,把PA0接到3.3V上,如图所示。此时,串口调试助手显示所测电压为3.299V~3.300V。
- 把PA0接到GND上,如所示图145所示。此时,串口调试助手显示所测电压为0.00V。
- 把PA0接到“温度/光照度传感模块”上的J10口,如下图所示,调整蓝色的电位器,即可观察到电压变化。该电位器的电路图如所示。
实验内容2:ADC内部温度传感
实验目的
- 了解和熟悉CortexM3的ADC基本特性
- 掌握STM32ADC的库函数编程方法
- 掌握GPIO管脚复用功能的使用方法
实验要求
利用ADC1的通道0,即PA0,采集输入电压。
实验步骤
- 搭建LED电路:
- 工程配置
- 源程序
#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--) ;
}
}
- 实验结果
思考与体会
我们通过使用ADC库函数设置其工作模式及通道,设置其通道规则实现采样,通过采样时间来确保其转换结果的稳定性。在实验中,我们从使用的PA0管脚引出一条杜邦线,可看做万用表的红表笔。
DAC实验
实验时间:2020.12.9
实验环境:NEWLab;M3核心模块
实验内容:DAC基础实验
实验目的
- 了解和熟悉CortexM3的DAC基本特性
- 掌握STM32DAC的库函数编程方法
- 掌握GPIO管脚复用功能的使用方法
实验要求
设置任何一个0-3.3V的电压数字,通过DAC1即PA4的输出管脚给出所设定的模拟电压。用万用表或者示波器观察输出电压情况。
实验步骤
- 搭建LED电路
- 配置工程
- 源程序
//步骤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); //启动转换
}
- 串口调试助手显示DAC输入数字和对应的DAC期望输出电压。用万用表测量PA4,其数值应该与串口调试助手输出一致。
思考与体会
串口中输出的是我们预期电压的理论值,是通过计算出来的,而为了验证我们可以使用两种办法:
1.直接使用万用表直观测量电压
2.与ADC模块结合,由于本次实验是DAC,即将数字转化为模拟,而正好可以通过上一个ADC实验实现模拟到数字的转换
本次实验使用的是第一种方式,直观对比,在电压较小时比较精确,随着电压的增加,会出现较小的误差
IIC串行RTC
实验时间:2020.12.9
实验环境:NEWLab;M3核心模块
实验内容: IIC串行RTC
实验目的
利用功能扩展模块中的时钟单元制作一个电子钟,能够通过M3核心模块将RTC芯片中的时间数据通过IIC总线读取出来,在LCD12864上显示当前时间,并能够通过串口助手下发时间调整命令对RTC芯片的时钟值进行调整。
实验步骤
-
准备源文件
-
添加源文件
-
配置工程
-
配置IIC
-
源程序
/* 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;
}
- 连接路线
7. 观察显示及串口
- 修改时间
思考与体会
对于这个实验,从原理上不太能理解,在老师解释后,着重研究了I2C的配置,但还是不太明白,于是上网百度进行了I2C的配置的一些学习:
IIC通信有3种类型的信号:开始信号,结束信号,和应答信号。
开始信号:SCL为高电平,SDA由高电平向低电平跳变,表示可以开始传输信号,进行通信了。
结束信号:SCL为高电平,SDA由低电平向高电平跳变,表示传输信号的时间已经过了。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。
CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,这就是开始信号。结束信号也一样,处理器不可能一直处于与其他IC通信的状态的。而应答信号,发送方把自己要发送的数据发送出去了,但不知道对方有没有收到,所以有些情况,需要等待接收方返回应答信号,告诉发送方我已经收到了,你可以继续发送下一条数据。