虽然这个充满信心的比赛最后由于一些***的原因自己几乎没有做交的白卷,但通过再一次的学习stm32,自己还是有不少的收获。
现把自己当时的笔记复制到这,作为一个记录以及交流,同时也作为第一篇博客:
一、 新建工程
略有不同的是程序的下载方法,需要装驱动。。。
二、 LED
电路原理图:
比赛板用的是stm32f103rb芯片,64管脚,LED与LCD同用了管脚PC8~PC15(对应LED1~LED8),所以增用了一个锁存器——M74HC573。在控制LED的时候,只需在输出对应电平后,给锁存器的LE(N—LE,对应PD2)管脚一个上升沿脉冲即可把对应的电平锁存到锁存器的输出端(Q1~Q8),从而控制LED。
需要注意的是:1、LCD的操作会影响LED的亮灭,如LCD的行写会点亮所有的LED,需进行程序处理;
2、对锁存器LE管脚的操作,给一个上升沿脉冲后应把对应的控制引脚(PD2)置低电平,以免LCD的操作影响LED的亮灭混乱。
有关蜂鸣器的使用比较简单,在这里一起说明:比赛板的蜂鸣器连在PB4上,
需注意的是Pb4管脚是JTAG调试接口之一,需要在程序中失能JTAG才可以使用Pb4管脚,如下语句: /* Disable the Serial WireJtag Debug Port SWJ-DP */
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
然而程序中用了这一句后,下次用JTAG下载程序时就下载不进去了,解决办法是:先按着开发板的RESET 按键别松手,再点击keil中的下载,然后松手按键,程序就可正常下载进去了。
三、 USART
CT1117开发板的USART2与下载程序的USB方口集成在一起,使用时只需装好对应的驱动即可实现电脑与开发板的串口通信(部分串口调试工具可能搜不到串口)。
比赛时对串口的使用方式可能性比较大的是,通过电脑发送指令控制stm32以执行相应操作,所以我结合LED编写了一个练习程序,使用串口发送指令控制LED亮灭,同时在LCD上显示最近一次所接收到的命令。串口的接收命令使用的是中断的方式,代码如下:
//配置串口2
void USART_Configuration(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
USART_InitTypeDefUSART_InitStructure;
NVIC_InitTypeDefNVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2;//PA2 Tx
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_3;//PA3 Rx
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate= 9600;
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_Rx | USART_Mode_Tx;
USART_Init(USART2,&USART_InitStructure);
USART_Cmd(USART2,ENABLE);//使能USART2
/*Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/*Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2,USART_IT_RXNE, ENABLE);//使能接受中断
}
//中断服务函数
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
/*Read one byte from the receive data register */
RxBuffer[RxCounter] = USART_ReceiveData(USART2);
if(RxBuffer[RxCounter]=='x')//指令格式led2_onx,以’x’作为命令的结尾
{
rxflag=1; //接受到命令的标志
RxCounter=0;
}
elseRxCounter++;
if(RxBuffer[0]!='l')//此句避免接受命令混乱
RxCounter=0;
}
USART_ClearITPendingBit(USART2,USART_IT_RXNE);//
}
//对于通过串口向电脑发送数据,采用printf()函数的重定向
int fputc(int ch,FILE *f)
{
USART_SendData(USART2,(uint8_t)ch);
while (USART_GetFlagStatus(USART2,USART_FLAG_TC) == RESET);
// delay_ms(10);
returnch;
}//库函数中有,复制即可使用
主函数省略,其中主要是调用了一个对命令处理的函数,每次调用处理函数,先判断命令接受的标志位,若置为1,则对存放命令的数组内容进行判断,从而操作LED并显示当前的命令。
Take care:
1、 sprintf();函数的使用:字符串转换函数 如sprintf(string,”%s%d%c”,”djkal”,a,buffer[5]);
其中string可以为u8string[20];或者u8 *string;类型;但是使用指针时程序总会卡死在这个函数中,调试时换为已指明大小的数组则避免了卡死问题;
2、 怀疑点:串口调试助手在发送数据到MCU时并不仅仅发送你输入的字符串,而有附加。
3、 通过电脑的串口助手或者超级终端发送指令控制开发板,指令最好自己定义格式。例如通过串口设置时间,可定义命令格式:A12:02:05B 其中’A’,’B’分别为命令的起始位和停止位,注意电脑发送的是字符串(ASCII码),受到数据后需要转换成数字,
如数字2和’2’相差48。若只是简单的控制不涉及数字,可以直接对字符进行识别即可。
四、 SysTick
不涉及系统而言,主要用于较精准的延时,用最新的3.5库实现非常容易。
首先调用SysTick_Config(SystemCoreClock/1000);其中SystemCoreClock是内核系统时钟频率,未做修改的话为72M(此函数并不关心此值大小),除以1000,则实现1ms中断一次。然后编写延时函数
void delay_ms(u16 nTime)
{
TimingDelay=nTime;//TimingDelay设置为全局变量
while(TimingDelay!=0);
}
中断服务函数
void SysTick_Handler(void)
{
if(TimingDelay>0)
TimingDelay--;
}
具体代码实现过程:在SysTick_Config中配置了SysTick的中断频率,每次进入中断函数都把全局变量TimingDelay减一,而在延时函数中不断循环查询TimingDelay变量,直至TimingDelay变为0延时结束。
有时需要考虑SysTick的中断优先级问题,如:如果EXTI 的优先级高于Systick中断,则在按键消抖时会造成,在延时10ms消抖的过程中会不断被按键电路的毛刺影响而多次重新进入按键中断,而每次都会重新调用延时10ms,等到按键松手后再延时10ms已经不能读到对应按键的电平状态,不能实现消抖。解决此问题的方法很简单,就是更改SysTick_Config函数中对中断优先级的配置使它高于按键的优先级,具体实现见EXTI_KEY中的讲述。(写的比较啰嗦,大致道理还是比较简单)
五、 EXTI_KEY
本次程序是在USART 的基础之上,加上利用按键控制Led,实现按键与串口同时控制lED的亮灭。较为简单,直接结合原理图给程序。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//切记打开复用时钟
//K1.K2.K3.K4分别连接stm32上的Pa0,Pa8,Pb1,Pb2,配置为上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0| GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1| GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//key1的配置,其他同样配置即可
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//key1
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿中断
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
void EXTI9_5_IRQHandler(void)//key2的中断函数,其他类似
{
if(EXTI_GetITStatus(EXTI_Line8)!= RESET)
{
delay_ms(10);//延时消抖,此函数用得是Systick的中断消抖,后面会讲注意问题
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)==0)
{
keyflag=1;//有按键按下的标志,按键处理函数中消除
key2flag=!key2flag;//具体按键的标志,具体按键处理后消除
}
While(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)==0);//松手检测
EXTI_ClearITPendingBit(EXTI_Line8);
}
}
按键的处理函数略,主要就是先判断按键标志keyflag以判断是否有按键按下,若有再继续辨别具体按键标志,控制Led。
Take care之处:
1、使用外部中断需要打开复用时钟;
2、消抖。当进入中断服务函数之后,先判断对应中断线的标志是否置位(能进入则是置位了的),然后延时10ms后,使用if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)==0)此句读取对应按键相连的管脚是否被外部按键拉低,以实现消抖。然而我们知道,延时函数使用的是Systick,它是通过在滴答定时器中断中递减相应变量实现延时的,对systick的配置我们通常使用的是Cm3内核函数uint32_t SysTick_Configmy(uint32_t ticks);具体函数代码如下:
/* Initialise the system ticktimer and its interrupt and start the
*system tick timer / counter in free running mode to generate
* periodical interrupts. */
static __INLINE uint32_tSysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload valueimpossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn,(1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 SystemInterrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL=SysTick_CTRL_CLKSOURCE_Msk|SysTick_CTRL_TICKINT_Msk|
SysTick_CTRL_ENABLE_Msk; /*Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
其中NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS)- 1); /* set Priority for Cortex-M0 System Interrupts */这一句是对Systick抢占优先级中断的配置,这里配置的是7,最低,我们需要把它修改为NVIC_SetPriority (SysTick_IRQn, 1);抢占优先级为1(只要比按键的中断优先级高即可)。注意比赛时工程中所使用的内核文件代码不允许修改,只要把这个函数复制到用户文件中,函数名稍做修改再调用即可。此时按键消抖稳定。
六、 LCD
主要总结、记录一些对官方提供LCD驱动代码的使用方法
rtccounter=RTC_GetCounter();
sprintf(string,"%d%s%d%s%d",rtccounter/3600,":",rtccounter%3600/60,":",rtccounter%60);
七、 ADC
Stm32f103rb内带2个12位的ADC(2个ADC的通道0~15在管脚上完全重合),每个ADC具有16个外部通道,时钟频率不能超过14M,竞赛板的ADC12的通道8(Pb0)与一个电位器相连接,所以在这里配置ADC的通道8为单通道连续转换模式,对ADC的配置如下:
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);//分频:72M/8=9MHz
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置为模拟输入模式
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//此处配置单通道独立模式或多通道扫描模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式禁止
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换使能
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//字节右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//转换通道数为1
ADC_Init(ADC1, &ADC_InitStructure);
/*ADC1 regular channel14 configuration */
ADC_RegularChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_55Cycles5);
//设置ADC通道转换
/*Enable ADC1 reset calibration register */
ADC_ResetCalibration(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)); //对ADC的校准,库中复制过来即可
/*Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);// 软件触发ADC转换开始
然后在主函数中通过函数不断查询的到转换结果并显示:
while(1)
{
ADCValue=ADC_GetConversionValue(ADC1)*3.30/0xfff;
sprintf(str,"%.3f",ADCValue);//注意此函数把浮点数转换成字符串的格式
LCD_DisplayStringLine(Line5,str);
}
八、 TIMER
Stm32的定时器是比较复杂,但绝不能想的太复杂,被官方给的一百余页介绍文档给吓倒。主要就这个这个原理图,看懂就全懂了。
对于一般的应用(时基计数、PWM输出、输入捕获),弄清楚三个寄存器即可,分别是自动重装载寄存器、CNT计数器、捕获/比较1234寄存器。
1、 时基计数功能:经过分频设置好时钟源后,CNT就开始(递增或递减)计数,若设置了更新中断,则当CNT递减至0时,产生更新中断,同时自动重装载寄存器的值会给到CNT计数寄存器,重新下一周期的计数。(设置为计数器递增模式时类似)。
具体对定时器的设置代码如下:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999;//设置自动重载值
TIM_TimeBaseStructure.TIM_Prescaler = 7199;//设置分频值:72M/(7199+1)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;//时钟分割,用于滤波,不用设0即可
TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_Cmd(TIM2, ENABLE);//使能Tim
此时定时器的基本设置已好,然后打开中断,在中断服务函数中设置计数变量累加,最简单的定时功能就实现了。。。。关于中断的频率,简单一句话:定时器以所设置的分频后的时钟频率,每计数至自动重载值(或由重载值减至零)时中断一次。。
//NVIC的设置省略
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能更新中断
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)== SET)//判断更新中断置位与否
{
Timcnt++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除更新中断
}
}
2、 PWM输出功能 从原理图上可以看出,每个定时器都有独立的四个通道,但是共用一个CNT,即对每个定时器的四个通道而言,其时基是相同的,不同的是各通道比较寄存器的值,从而每个定时器都可产生4路频率相同而占空比不同的PWM波(特别注意这里是对每一个定时器而言的)。
具体原理过程是,CNT以设置好的频率计时,CNT的数值范围是0至重载寄存器的值(即PWM的周期),对PWM模式1且输出极性为高(。。。)而言,当CNT的值在0至 [捕获寄存器值]时,输出高点平,当CNT的值在[捕获寄存器值]至[重载寄存器值]时,输出低电平。所以不难看出,通过对预分频寄存器、自动重载寄存器、比较寄存器进行设置,即可生成不同频率不同占空比的PWM。
具体代码如下:
//对时基的设置同前…。。。
//每个TIM通道都对应一个外部GPIO,对应设置为GPIO_Mode_AF_PP复用推挽输出模式(略)
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode =TIM_OCMode_PWM1;//PWM输出模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 300;//设置比较寄存器的值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
完毕。。
若在程序运行中要更改占空比,调用如下函数:
TIM_SetCompare2(TIM2,x);// 通过设置比较寄存器的值为x 来调节占空比,配置为PWM模式1且输出极性高时,x为高电平
3、输入捕获功能 可以测量脉冲频率、脉冲宽度
同样,对应每个TIM有四个通道,每个定时器有四个输入捕获通道。同样每个tim的四个通道对应相同的时基。输入捕获的实现原理是:首先设置好捕获上升沿还是捕获下降沿的脉冲,每当捕获到对应的脉冲边沿时,CNT计数寄存器会把它的当前值存到捕获寄存器中,同时可以产生中断。、根据这个原理,我们在中断中对捕获寄存器的值进行处理,即可得到脉冲的宽度或者脉冲的周期。具体方法是:1.在中断服务函数中设置变量,计算出两次捕获的CNT的差值,与相关计数频率相结合计算,即可算出脉冲频率;2.如果开始捕获的是上升沿,在中断中记录捕获寄存器的值为a,然后设置为下降沿捕获,再次中断记录捕获寄存器的值b,就可以计算出高点平脉冲宽度为:(b-a)/[计数频率]。实现代码如下:
//对应管脚设置为浮空输入模式
//时基的设置同前
//设置输入捕获
TIM_ICInitStructure.TIM_Channel =TIM_Channel_2;//选择定时器的通道
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//输入不分频,分频即捕获到若干次中断一次
TIM_ICInitStructure.TIM_ICFilter = 0x0;//不滤波,
TIM_ICInit(TIM3, &TIM_ICInitStructure);
基本设置好后使能中断及定时器
/* TIM enable counter */
TIM_Cmd(TIM3, ENABLE);
/*Enable the CC2 Interrupt Request */
TIM_ITConfig(TIM3, TIM_IT_CC2|TIM_IT_Update, ENABLE)//捕获中断与更新中断都
打开
//中断服务函数,处理捕获到的当前CNT寄存器的值
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)== SET)
{
updaflag++;//记录更新次数
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除更新中断
}
if(TIM_GetITStatus(TIM3,TIM_IT_CC2)== SET)
{
if(capture1==0)//第一次捕获
{
capture1=TIM_GetCapture2(TIM3);
}
Else//capture1不为0,则说明第一次已经捕获,进行第二次捕获
{
capture2=TIM_GetCapture2(TIM3);//读取捕获寄存器值
capture=capture2-capture1+updaflag*65536;//两次捕获只差,加上更新数
capture1=0;//清除第一次捕获标志,
updaflag=0;//清除更新次数
}
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2); //清除更新中断
}
}
完毕
看别人写的总会感觉写的过于复杂,自己想总结写得直白简单些,然而还是写的这么啰嗦的样子,好吧。先这样吧
但还是再整理下有关定时器的库函数吧:
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);//时基配置函数
TIM_ITConfig(TIM3, TIM_IT_CC2|TIM_IT_Update,ENABLE);//使能中断
TIM_Cmd(TIM2, ENABLE);//使能Tim
void TIM3_IRQHandler(void)//中断服务函数
if(TIM_GetITStatus(TIM3,TIM_IT_Update) ==SET)//判断相应中断位
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志
//以上实现基本定时功能
TIM_ICInit(TIM3, &TIM_ICInitStructure);//输入捕获的配置
TIM_GetCapture2(TIM3);//读取定时器X的通道n
TIM_SetCompare2(TIM2, TIMCompare2);//设置Tim2的比较寄存器2的值,可灵活调整
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Disable);//使能或者使能TIMx在OC1
上的预装载寄存器
使能TIM3在CCR2上的预装载寄存器,即TIM3_CCR2的预装载值在更新事件到来时才能被传送至当前寄存器中。
Pwm占空比
TIM_ARRPreloadConfig(TIM3, ENABLE);//使能定时器的预装载寄存器
基本的应用只涉及到以上这些库函数,需要搞清楚每个函数的功能及所操作的是哪个寄存器,把这些函数以及函数的参数真正细心搞懂,Tim的结构和使用方法也就差不多了,提示一句,注意这些函数的组织结构。比如TIM_OC2Init()类似共有四个,是以每个定时器有四个通道划分编写的,参数中指明定时器x,即实现了定期器x通道2的配置。其他配置函数的组织方式类似。。。。。。。。。。2015/3/22
注意点:一定要注意数据类型,否则结果很郁闷2015/4/6
九、RTC
竞赛板的VBAT与供电电源相连如图,即无后备电源,也没有外部时钟LSE,所以掉电后Rtc数据将丢失。
竞赛题目应该不会涉及到RTC吧 ,或者只是简单的实现时分秒时间,万万不可能把日历涉及到的,不然整个比赛的5个小时都去整时钟去了。
所以下面简单实用Rtc实现基本时钟功能。利用库直接复制粘贴基本就可以了。首先找到Example中的RTC文件夹,里面有两个例程 ,选择使用时钟源为内部低速时钟的LSI_Calib文件夹,在main()函数中复制void RTC_Configuration(void)函数,不做任何修改,调用即可实现对Rtc的配置。然后调用RTC_GetCounter()和RTC_SetCounter()函数实现对当前时钟的读取和设置。通常使用会配置Rtc的秒中断RTC_ITConfig(RTC_IT_SEC, ENABLE)然后在RTC的中断中设置标志位,通过对秒中断标志位的查询实现时间显示1S更新一次。
再次感觉,开发板没有后备电源和外部低速时钟,对RTC 涉及的使用简单多了。
十、I2C_EEPROM
把对EEPROM的使用放到了最后,是因为最开始以为比赛的时候不给iic的程序,费了比较多的时间来纠结这块的问题,首先是本人曾试着利用stm32的硬件iic驱动EEPROM,但自认为懂了后编程一次没成功后就再没试过,一直用的模拟iic,但是比赛要是自己模拟的iic再操作EEPROM的话需要花费三四十分钟的时间(自己默默敲了两三遍,感觉很熟练时也要这么久),然而郁闷的是还会出现未知问题导致EEPROM写不上读不出。。。好在好在后来得知比赛时给模拟的iic驱动代码,真是省了一大麻烦,可以用更多地时间设计实现更多的系统功能,组委会是明智的。
有了i2c的驱动代码,直接根据At24c02的数据手册中的时序图编写两个函数即可实现对EEPROM的简单读写操作,根据两年的官方模拟题可以看出,比赛时对EEPROM的使用只是存储简单的几个数据,有了这两个操作函数足矣。
voidAT24C02_ByteWrite(u8 addr,u8 dat)//写一字节dat到EEPROM的内部地址addr(0~255)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
u8 AT24c02_ByteRead(u8addr)//从EEPROM的内部地址addr读出一字节
{
u8 dat=0;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
dat=I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return dat;
}
需注意:EEPROM的读写较慢,需要延时,根据数据手册5ms即可。
拿到学校提供的开发板后,开始练习并作了如上的记录。主要是结合了近两年的模拟试题、考试时提供的资料、以及开发板的特点而做了一些简单的练习记录,并不具有通用性。通过练习再一次的复习了stm32的相关知识,也熟悉了竞赛板的使用。至此告一段落。祝自己好运!!
12电信 sdl 2015/3/24