打印

【连载】STM32f107&f407开发板(科星F107)学习笔记基础篇(更新中

[复制链接]
楼主: ren0zhe
手机看帖
扫描二维码
随时随地手机跟帖
41
MARK

使用特权

评论回复
42
ren0zhe|  楼主 | 2013-10-6 08:46 | 只看该作者

第七节   串口学习—查询输入输出


本节主要讲了,使用库函数来获取串口的一些标记位的函数的用法,及接收和发送字符串函数的用法等


详细内容,请查阅附件:

pdf版学习笔记
7科星F107开发板学习笔记—串口学习之查询输入输出.pdf (257.86 KB)


源码实例工程文件
7科星F107开发板学习笔记源码—串口学习之查询输入输出.rar (2.71 MB)

视频教程,请在下面网盘的链接中下载
http://pan.baidu.com/share/link?shareid=1646662150&uk=2886683799




部分内容如下:
首先,这里跟上一节一样需要添加外设库文件stm32f10x_usart.c,该文件的路径为“\Libraries\STM32F10x_StdPeriph_Driver\src”。
本节完整代码如下:
/* Includes------------------------------------------------------------------*/
#include "stm32f10x.h"
/* Private function prototypes-----------------------------------------------*/
void GPIO_Configuration(void);
USART_InitTypeDefUSART_InitStructure;
char chars;
int main(void)
{
USART_InitTypeDef USART_InitStructure;
/* System Clocks Configuration */
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1| RCC_APB2Periph_AFIO,ENABLE);
/* Configure the GPIO ports */
GPIO_Configuration();
/* USARTy and USARTz configuration------------------------------------------------------*/
/*
115200  8  n 1
*/
USART_InitStructure.USART_BaudRate = 115200;
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(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
while (1)
{
     while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET)//等待接收为空
   {
   }
  
   chars = (USART_ReceiveData(USART1)); //接受字符
   
   USART_SendData(USART1, chars);//发送接收到的字符
      
   while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)//等得发送完成
   {
   }  
}
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* Configure USARTx_Tx as alternate function push-pull */
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);
/* Configure USARTx_Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
前面的配置代码这里就略过了,第六节都提到过了,下面主要讲一下,下面的主循环部分的函数:
while (1)
{   
   while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)//接收为空
    {
    }
   
   chars = (USART_ReceiveData(USART1)); //接受字符
   
   USART_SendData(USART1, chars);//发送接收到的字符
     
   while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)//等得发送完成
    {
   }  
  }
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)”这个函数是调用外设串口库中的函数,我们可以右击“USART_GetFlagStatus”点“Go to Definition of USART_GetFlagStatus”或者点“Go toDeclaration of USART_GetFlagStatus”,找到函数的定义部分如下:
                              
我们主要看这几句话:
  *@brief  Checks whether the specifiedUSART flag is set or not.
  *@param  USARTx: Select the USART or theUART peripheral.
*    @arg USART_FLAG_RXNE: Receive data register not empty flag
这里的意思就是获取接收数据寄存器不为空的标志。
当串口的接收寄存器为空时就在while()循环中一直循环,当不为空时,跳出这个while()循环,执行下面的读取数据和发送数据的操作。
同样的方法,我们可以知道函数USART_ReceiveData()USART_SendData()
USART_GetFlagStatus()的作用和用法了,这里就不在赘述了,强悍的库函数的注释都写的很清晰了。
下面就是下载程序到开发板,然后打开串口调试助手(这里的USB转串口驱动和串口调试助手程序请在第六节中查找,里面有下载的路径)。
         打开调试助手,在字符串输入框中输入字符,然后点击“发送”,就会在接收区接收到发送的数据,见下图:
    接收数据和发送数据就到这里了。

使用特权

评论回复
43
ren0zhe|  楼主 | 2013-10-9 15:43 | 只看该作者
第八节    串口学习之中断输入输出

      本节主要讲了 使用 中断的方式 进行串口数据的发送与接收。

pdf版学习笔记如下:
8科星F107开发板学习笔记—串口学习之中断输入输出.pdf (190.49 KB)

源码工程文件
8科星F107开发板学习笔记源码—串口学习之中断输入输出.rar (3.18 MB)



部分内容如下:

中断是在任务稍多的项目中经常使用的一种程序运行方式,他在一定程度上保障了程序的实时性。这里我们介绍一下串口的中断发送和接收。
串口的中断接收,首先我们应该想到的是:我们怎么知道数据接收完了呢?
一般情况下有两种方式:
1、规定带有帧结束标志的数据,比如一些串口功能器件发来的数据 我们应如何判断一个完整的数据 以便后续的分析呢,我们拿gps模块为例,gps的数据的个数是不断变化的,因为很多参数的数据是不固定的,怎样才能一帧一帧的接收分析呢?经常用的就是判断数据的开始和结束标志
2、根据帧间隔时间。  很多数据采集设备发来的数据都是隔一段时间传输一次 这样的数据我们就可以根据字节间的时间间隔来判断一帧的结束,比如一个连续的数据帧的字节间隔是固定的,这个时间由波特率决定,比如时间算出来是T1 那么我们就可以认为在大于1.5 T1间隔没有新数据收到就认为一帧的接收结束。

我们的工程测试程序是根据第2种来实现串口的中断接收,这里因为是测试程序,所以帧判断的tim时间设为1s,也就是说只要你发送的数据间隔小于1s 我们都认为是一帧的数据。

下面我们开始代码的编写,和前面一样,使用第二节新建的空工程,定时器和NVIC的配置、串口引脚的配置和串口参数的配置前面几节中我们都讲过了,这里就不多说了,不是很了解的请看前面章节。我们来着重看下面的代码。第二节空工程的代码请在下面的路径下载:  http://pan.baidu.com/share/link?shareid=270259771&uk=2886683799

首先,这里需要添加外设库文件stm32f10x_usart.c和stm32f10x_tim.c,文件的路径为“\Libraries\STM32F10x_StdPeriph_Driver\src”。

完整的main.c文件代码如下,所有的配置代码都在这个文件下面
/* Includes------------------------------------------------------------------*/
#include "stm32f10x.h"

u8Uart_send_counter; //Uart_send()函数发送的字节数
vu8*Uart_send_pointer;//发送的数组指针

/* Privatevariables ---------------------------------------------------------*/
USART_InitTypeDefUSART_InitStructure;

/* Privatefunction prototypes -----------------------------------------------*/

voidGPIO_Configuration(void);
voidNVIC_Configuration(void);
voidTIM2_Configuration(void);

/* Privatefunctions ---------------------------------------------------------*/

/**
  * @brief  Main program
  * @param None
  * @retval None
  */
int main(void)
{     
  /* System Clocks Configuration */
   SystemInit();
   
  TIM2_Configuration();   
  /* NVIC configuration */
  NVIC_Configuration();

  /* Configure the GPIO ports */
  GPIO_Configuration();

/* USARTy andUSARTz configuration ------------------------------------------------------*/
  /* 9600 8  n  1
  */
  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;

  /* Configure USARTy */
  USART_Init(USART1, &USART_InitStructure);
  /* Configure USARTz */

  
  /* Enable USARTy Receive and Transmitinterrupts */
  USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);
// USART_ITConfig(USART1, USART_IT_TXE,ENABLE);//使能中断这里先不使能  在后面中断发送之前发送
  
  USART_Cmd(USART1, ENABLE);

  while (1)
  {
  }
}

voidGPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

  /* Configure USARTx_Tx as alternate functionpush-pull */
  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);

  /* Configure USARTx_Rx as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}
/*******************************************************************
* FunctionName  : Uart_InterruptsendY 带字节传输
*Description    : 中断发送字符串
* Input          : 参数一:uart1 2 3 4.。
                   参数二:要发送的字符串地址指针
                   参数三:要传输的字节数
* Output         : 无
* Return         : 无
*******************************************************************************/
voidUart_InterruptsendY(USART_TypeDef *USARTx,vu8* Uart0_sended,u8 NbOfBytes )
{   
   Uart_send_counter=NbOfBytes;
   Uart_send_pointer=Uart0_sended;
   USART_SendData(USARTx,*Uart_send_pointer++);
   USART_ITConfig(USARTx, USART_IT_TXE,ENABLE);
   //UART_ITConfig(UARTx,UART_IT_TxEmpty,ENABLE);
}

voidTIM2_Configuration(void)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

  // TIM2 Configuration:向上计数中断:
   
  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 7200-1;
  TIM_TimeBaseStructure.TIM_Prescaler =10000-1;//
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode =TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
  TIM_ITConfig(TIM2, TIM_IT_Update , ENABLE);//开启计数中断
  /* TIM2 enable counter */
  //TIM_Cmd(TIM2, ENABLE);//开启时钟 这里不开启 在接受到数据时开启 来判断是不是帧结束
}
/**
  * @brief Configures the nested vectored interrupt controller.
  * @param None
  * @retval None
  */
voidNVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Configure the NVIC Preemption PriorityBits */  
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* Enable the USARTy Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel =USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}


定时器中断和串口中断处理函数都在stm32f10x_it.c文件下面,代码如下:
void TIM2_IRQHandler(void)
{/******处理完数据后一定要把Uart0_rev_count清零,
        但是为了安全一开始就请零可能更好,这样的话
        我们规定用Uart0_rev_countmap来代替Uart0_rev_count********************
   u32 Uart0_rev_countmap;
   
  /***清楚tim中断和关闭tim中断,在uart接受中断里  有相应的使能****/
   TIM_Cmd(TIM2, DISABLE);
   TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
/****copy Uart0_rev_count的值****/
  Uart0_rev_countmap=RxCounter1;
   RxCounter1=0;
  /*****测试语句  返回接受的语句  顺便测试中断发送
         这里以后是数据处理函数***********************/
  Uart_InterruptsendY(USART1,RxBuffer1,Uart0_rev_countmap);
}

void USART1_IRQHandler(void)
{
  if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
  {
    TIM_Cmd(TIM2, ENABLE);
    TIM2->CNT &=0x0000;//定时器延时设置  每次接受清零 当接受间隔超过定时器定的值时  进入tim中断  认为一帧接受完成
   
    RxBuffer1[RxCounter1++] =USART_ReceiveData(USART1);
   USART_ClearITPendingBit(USART1, USART_IT_RXNE);
  }
  
  if(USART_GetITStatus(USART1,USART_IT_TXE) != RESET)
  {   
     Uart_send_counter--;
    if(Uart_send_counter>0)
     {
       USART_SendData(USART1,*Uart_send_pointer++);
     }
     else
       USART_ITConfig(USART1,USART_IT_TXE, DISABLE);
  }
}

  

使用特权

评论回复
44
trumpxp| | 2013-10-9 16:58 | 只看该作者
很不错的学习帖子  楼主   很不错  很给力   资料收藏一个    谢谢

使用特权

评论回复
45
trumpxp| | 2013-10-9 16:59 | 只看该作者
这一块   感觉自己了解的还是不多  需要仔细玩玩   看看   有时间入手一块开发板  仔细学习学习  谢谢了

使用特权

评论回复
46
ren0zhe|  楼主 | 2013-10-10 08:29 | 只看该作者
第九节   外部中断的学习


这里使用按键当做外部输入信号,通过GPIO口来触发MCU的外部中断,中断处理就是点亮或熄灭LED灯。
     要实现按键对LED灯的控制,需要以下几步:
1、  配置LED灯的GPIO口,为输出
2、  配置按键输入的GPIO口,为输入
3、  这里用到了中断,就需要配置NVIC,来规定出中断的优先级
4、  做出中断处理函数


pdf版学习笔记
9科星F107开发板学习笔记—外部中断的学习.pdf (168.6 KB)

源码工程文件
9科星F107开发板学习笔记源码—外部中断的学习.rar (2.95 MB)



笔记部分内容如下:
输入GPIO口配置
由前面的原理图可知 三个按键分别连接 PC6,PD13和PD14引脚。配置时与输出类似,这里需要先打开时钟,GPIO口模式配置为浮空输入模式,然后对所用的GPIO口进行初始化,代码如下:
/* Enable GPIOBclock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD,ENABLE);
/* Enable AFIOclock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  /* Configure PB.09 pin as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
下一步选择外部中断线
Selects the GPIO pin used as EXTI Line”,使用函数GPIO_EXTILineConfig()来进行配置选择,代码如下:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource6);//选择PC6
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource13); //选择PD13
GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource14); //选择PD14
配置外部中断
像配置GPIO口一样使用库函数提供的结构体来配置,定义:EXTI_InitTypeDef  EXTI_InitStructure;来使能外部中断的线和配置中断模式和触发方式,代码如下:
EXTI_InitStructure.EXTI_Line =EXTI_Line6|EXTI_Line13|EXTI_Line14;
  EXTI_InitStructure.EXTI_Mode =EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Rising;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
NVIC配置
因为这里是外部中断,前面讲过是中断就都要配置NVIC的,配置的方式也都是一样的,先选择群组,再选择中断通道,优先级等,这里就不多说了,代码如下:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  /* Enable and set EXTI9_5 Interrupt to thelowest priority */
  NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  NVIC_InitStructure.NVIC_IRQChannel =EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
  NVIC_Init(&NVIC_InitStructure);
写到这里,外部中断的配置就完成了。
前面写的有点乱,下面我们给出main.c文件的完整代码:
/* Includes------------------------------------------------------------------*/
#include"stm32f10x.h"
EXTI_InitTypeDef   EXTI_InitStructure;
GPIO_InitTypeDef   GPIO_InitStructure;
NVIC_InitTypeDef   NVIC_InitStructure;
voidEXTI_key_Config(void);
voidLED_GPIOconfig(void);
int main(void)
{
  SystemInit();
  LED_GPIOconfig();
  EXTI_key_Config();
  
  while (1)
  {
  }
}
voidEXTI_key_Config(void)
{
  /* Enable GPIOB clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD,ENABLE);
/* Enable AFIOclock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  /* Configure PB.09 pin as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
  
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13|GPIO_Pin_14;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  
  /* Connect EXTI9 Line to PB.09 pin */
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource6);
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource13);
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource14);
  /* Configure EXTI9 line */
  EXTI_InitStructure.EXTI_Line =EXTI_Line6|EXTI_Line13|EXTI_Line14;
  EXTI_InitStructure.EXTI_Mode =EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Rising;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  /* Enable and set EXTI9_5 Interrupt to thelowest priority */
  NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  NVIC_InitStructure.NVIC_IRQChannel =EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
voidLED_GPIOconfig(void)
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  /* Configure PD0 and PD2 in output pushpullmode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 |GPIO_Pin_4|GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}
下面我们给出外部中断的处理函数,作用就是按键按下一次,对应的LED灯点亮或熄灭。代码如下:
voidEXTI9_5_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line6) != RESET)
  {
    GPIO_WriteBit(GPIOA, GPIO_Pin_5,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5)));
    /* Clear the  EXTI line 9 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line6);
  }
}
voidEXTI15_10_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line13) != RESET)
  {
    GPIO_WriteBit(GPIOA, GPIO_Pin_6,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_6)));
    /* Clear the  EXTI line 9 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line13);
  }
  if(EXTI_GetITStatus(EXTI_Line14) != RESET)
  {
    GPIO_WriteBit(GPIOA, GPIO_Pin_7,(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_7)));
    /* Clear the  EXTI line 9 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line14);
  }
}
之后我们可以将代码烧录到开发板上,然后看一下效果。
正确的效果如下:
按下按键K1,会点亮LED灯D3,再次按下K1,会熄灭D3;
按下按键K2,会点亮LED灯D2,再次按下K2,会熄灭D2;
按下按键K3,会点亮LED灯D1,再次按下K3,会熄灭D1。
外部中断就讲到这里了。

使用特权

评论回复
47
atianjun| | 2013-10-11 10:51 | 只看该作者
F4系列的什么时候出

使用特权

评论回复
48
ren0zhe|  楼主 | 2013-10-11 19:40 | 只看该作者
atianjun 发表于 2013-10-11 10:51
F4系列的什么时候出

得等F107系列的更新完,你可以先看一下,之前F407的,有实例工程,在百度网盘中下载,链接如下
http://pan.baidu.com/share/link?shareid=1531243811&uk=2886683799

使用特权

评论回复
49
hkcj| | 2013-10-11 20:32 | 只看该作者
这么多好资料   楼主   谢谢了   这一块   感觉自己的很多的东西   都掌握的不是很踏实   需要好好看看  谢谢了  

使用特权

评论回复
50
ren0zhe|  楼主 | 2013-10-12 08:52 | 只看该作者
hkcj 发表于 2013-10-11 20:32
这么多好资料   楼主   谢谢了   这一块   感觉自己的很多的东西   都掌握的不是很踏实   需要好好看看  谢 ...

多谢支持

使用特权

评论回复
51
outstanding| | 2013-10-12 10:30 | 只看该作者

使用特权

评论回复
52
ren0zhe|  楼主 | 2013-10-12 21:55 | 只看该作者
outstanding 发表于 2013-10-12 10:30

使用特权

评论回复
53
cjhk| | 2013-10-13 10:33 | 只看该作者
很不错的东西   需要好好学习学习   感觉自己这一块的差距好大阿   必须要仔细钻研钻研   顶一个先   楼主

使用特权

评论回复
54
ren0zhe|  楼主 | 2013-10-14 08:55 | 只看该作者
cjhk 发表于 2013-10-13 10:33
很不错的东西   需要好好学习学习   感觉自己这一块的差距好大阿   必须要仔细钻研钻研   顶一个先   楼主  ...

多谢支持

使用特权

评论回复
55
ren0zhe|  楼主 | 2013-10-15 09:25 | 只看该作者
第十节 ADCDMA方式获取值)的学习

学习之前我们了解下,在这一节我们将学到的几个重要的点:
1、  ADC的时钟配置
2、  为什么要用DMA方式读取ADC的数据
3、  外设地址和数据寄存器的地址是根据什么定义的
4、  ADCDMA的参数配置
这几点我们都能在下文中找到答案,请仔细观看。


学习笔记pdf版
10科星F107开发板学习笔记—ADC(DMA方式获取).pdf (740.02 KB)

工程源码文件
10科星F107开发板学习笔记—ADC(DMA方式获取).rar (3.26 MB)


笔记的部分内容如下:

下面我们看一下硬件原理图,科星F107开发板使用一个可调电阻器来当做模拟信号通过PC4端口输入到MCU中,然后对采集到的数据进行一个简单的计算,再通过串口打印到电脑上,便于我们的调试。硬件原理图如下:

由于这里我们用到了串口打印数据,所以要注意下面跳线的地方要短接好
跳线:短接图示的引脚
如图:





下面我们主要讲的是:
1、 ADC的配置及使用
2、 DMA的配置和使用


下面我们来进行编码的学习
首先我们打开,第二节中新建的空工程,没有的朋友可以在下面的链接中找到:
下面我们打开这个工程,
第一步,由于我们这里要使用到库文件中串口配置的函数,这里需要添加外设库文件
stm32f10x_adc.cstm32f10x_dma.cstm32f10x_usart.c,文件的路径为
\Libraries\STM32F10x_StdPeriph_Driver\src”,添加完成后,如下图


第二步,和第六节时的一样,右击工程文件名,打开“Options”,找到“Gerenal Options”中的“Library Configuration”,在Library选项选择“Full”,如下图:


第三步,添加C语言标准输入输出的头文件#include <stdio.h>,其余的关于printf函数的代码就不多说了,不了解的请看第六节串口输出的内容。

第四步,定义好ADC的规则数据寄存器首地址,这里存储了规则转换的数据。定义的地址如下:#define ADC1_DR_Address    ((uint32_t)0x4001244C)
地址0x4001244C不是自己随机定义的,而且在芯片的寄存器手册中都有定义,没有芯片的寄存器手册的朋友请在下面链接下载:
下面我们细讲一下ADC_DR寄存器的地址的查找过程:
1)找到手册最开始处的存储器映像图,里面规定好了各个外设的起始地址的区间,里面规定好了ADC1的起始地址在区间0x4001 24000x4001 27FF内。


2)根据上表的提示,在11.12.15节找到ADC各个寄存器的便宜地址,可以看到ADC_DR的便宜地址是4Ch,这样就可以确定了ADC_DR的地址:
ADC_DR的地址 = 起始地址 + 偏移地址
0x4001 244C   = 0x4001 2400+  4C

3ADC_DR寄存器的偏移地址也可以在该寄存器的讲解处找到,如下图:


补充一点:在使用DMA传输数据的时候,需要设置外设地址和存储器地址,外设地址当然就是ADC的地址了,而存储器的地址如果使用8位数据的话,存储器必须定义为8位缓冲区;如果使用16位数据格式的话,存储器则为16位缓冲器,不可定义为32位或更多,否则,数据将出错。

ADC配置
ADC的配置过程
1)开启PC口时钟,设置PC4为模拟输入。
2)使能ADC1时钟,并设置分频因子。
ADC时钟是由PCLK2经分频产生。如下图所示:

配置ADC时钟的函数RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC时钟(ADCCLK),因为我们的APB2时钟为72M,如果不对ADC时钟进行设置的话,这里就默认为2分频,如下图,(这是在芯片的寄存器手册的RCC寄存器处截取的图片),这样得到的ADC时钟为36M,远远超过了小于14M的规定,这样就会大大影响ADC的精度,所以我们这里采用6分频得到12M的ADCCLK时钟。

这个配置应该放在函数SetSysClockTo72()里面,但这里方便起见,我放在了RCC配置的函数里面。代码如下:
void RCC_Configuration(void)
{
  SystemInit();

/*ADCCLK = PCLK2/6 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC时钟(ADCCLK

  /* Enableperipheral clocks ------------------------------------------------*/
  /* Enable DMA1clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

  /* Enable ADC1and GPIOC clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC,ENABLE);
}

3)设置ADC1的工作模式。
在设置完分频因子之后,我们就可以开始ADC1的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。
4)设置ADC1规则序列的相关信息。
接下来我们要设置规则序列的相关信息,我们这里只有一个通道,并且是单次转换的,所以设置规则序列中通道数为1,然后设置通道1的采样周期。
5)开启AD转换器,并校准。
在设置完了以上信息后,我们就开启AD转换器,执行复位校准和AD校准,注意这两步是必须的!不校准将导致结果很不准确。
6)读取ADC值。
在上面的校准完成之后,ADC就算准备好了。接下来我们要做的就是设置规则序列1里面的通道,然后启动ADC转换。在转换结束后,读取ADC1_DR里面的值就是了


下面我们来进行ADC的配置,首先打开ADC的时钟,这里因为ADC用的是PC4端口,所以也需要打开GPIOC的时钟,代码如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1| RCC_APB2Periph_GPIOC, ENABLE);
         打开所需的外设时钟之后,我们来对ADC进行配置,配置的方式也是跟其它外设一样,进行“Init structure definition”,也就是使用库函数提供的一个结构体进行定义,简单方便易懂,下面我们看配置的代码:

  /*ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode =ADC_Mode_Independent;
第一个配置为 ADC模式的配置,在寄存器的位置为11.12.2ADC控制寄存器1(ADC_CR1),如下图,

这里我们选择的是独立模式“ADC_Mode_Independent”,这个量的定义我们可以右击该词,然后“Go to …”找到定义的位置,如下图。在这个模式下,双ADC不能同步,每个ADC接口独立工作。所以如果不需要ADC同步或者只是用了一个ADC的时候,就应该设成独立模式了。


ADC_InitStructure.ADC_ScanConvMode= ENABLE;
该句代码是指定是否允许ScanSingle模式间的切换,ENABLE为允许,DISABLE为不允许。英文解释为“Specifies whether theconversion is performed in Scan (multichannels) or Single (one channel) mode”。使用一个ADC的话,DISABLE也是可以的,但是同时使用多个ADC,必须配置为ENABLE
  ADC_InitStructure.ADC_ContinuousConvMode =ENABLE;
英文解释为“Specifies whether the conversionis performed in Continuous or Single mode”。这里设置为ENABLE,即连续转换。如果设置为DISABLE,则是单次转换。两者的区别在于连续转换直到所有的数据转换完成后才停止转换,而单次转换则只转换一次数据就停止,要再次触发转换才可以。所以如果需要一次性采集1024个数据或者更多,则采用连续转换。
  ADC_InitStructure.ADC_ExternalTrigConv =ADC_ExternalTrigConv_None;

英文解释为“Defines the external trigger usedto start the analog to digital conversion of regular channels.”。这里的作用是选择启动规则通道组转换的外部事件,事件可以分为三个大类:                    
第一种是最简单的软件触发(SWSTART),参数为ADC_ExternalTrigConv_None。设置好后还要记得调用库函数ADC_SoftwareStartConvCmd(ADC1,ENABLE);这样触发才会启动。

第二种是定时器通道输出触发。共有这几种:
ADC_ExternalTrigConv_T1_CC1、
ADC_ExternalTrigConv_T1_CC2、
ADC_ExternalTrigConv_T1_CC3、
ADC_ExternalTrigConv_T2_CC2、
ADC_ExternalTrigConv_T3_TRGO
ADC_ExternalTrigConv_T4_CC4。

定时器输出触发比较麻烦,还需要设置相应的定时器。定时器触发的设置有兴趣的朋友可以自己研究一下,这里不做展开研究了;
第三种是外部引脚触发,对于规则通道,选择EXTI线11和TIM8_TRGO作为外部触发事件
更加详细的内容请看芯片的寄存器手册,如下图:


  ADC_InitStructure.ADC_DataAlign =ADC_DataAlign_Right;
英文解释为“Specifies whether theADC data alignment is left or right.”这里设置为ADC_DataAlign_Right右对齐方式。建议采用右对齐方式,因为这样处理数据会比较方便。当然如果要从高位开始传输数据,那么采用左对齐优势就明显了。

  ADC_InitStructure.ADC_NbrOfChannel = 1;
英文解释为“Specifies the number ofADC channels that will be converted using the sequencer for regular channelgroup.”也就是指定ADC通道的数量。要是到多个通道采集数据的话就得设置一下这个参数。此外在规则通道组的配置函数中也将各个通道的顺序定义一下,如:
      /*ADC1 regular channel14 configuration */
ADC_RegularChannelConfig(ADC1,ADC_Channel_14,1, ADC_SampleTime_55Cycles5);      

以上ADC各个配置的参数的详细介绍请看芯片的寄存器手册,例如下图所示外部触发转换部分的介绍:
   
  ADC_Init(ADC1, &ADC_InitStructure);
//用前面设定好的结构体内的各个参数对ADC进行初始化
  /*ADC1 regular channel14 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1,ADC_SampleTime_55Cycles5);
配置ADC各个通道的转换顺序,由于这里我们只用到了1ADC通道14,所以这里是可以省略的,但是用到大于2ADC通道的话,这里就必须规定出各个通道的转换顺序了,例如:
我们现在用到了4ADC输入通道,分别为02914,转换顺序分别为20914,假设采用间隔为71.5clk,其设置如下:
ADC_InitStructure.ADC_NbrOfChannel = 4;   //4个通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_2 , 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0 , 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_9 , 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14 , 4, ADC_SampleTime_55Cycles5);


使用特权

评论回复
56
Rain_King| | 2013-10-15 10:33 | 只看该作者
不错啊..........必须学习啊.......

使用特权

评论回复
57
ren0zhe|  楼主 | 2013-10-16 09:28 | 只看该作者
Rain_King 发表于 2013-10-15 10:33
不错啊..........必须学习啊.......

多谢支持

使用特权

评论回复
58
ren0zhe|  楼主 | 2013-10-16 14:37 | 只看该作者
本帖最后由 ren0zhe 于 2013-10-16 14:40 编辑

第十一节  内部flash模拟EEPROM存读取数据的学习



用flash模拟EEPROM的原因:
      在许多应用场合下需要用EEPROM保存非易失性的数据,但是意法半导体为了控制成本,没有在STM32F10X系列芯片中集成EEPROM,所以我们就需要用其内部集成的FLASH通过软件模拟EEPROM来达到同样的效果。


学习笔记中主要讲解了 flash 存储读取数据的机制及使用库函数在flash上存储读取数据的方法。


学习笔记pdf版
11科星F107开发板学习笔记—内部flash模拟EEPROM存读取数据的学习.pdf (497.7 KB)

源码工程文件
11科星F107开发板学习笔记源码—内部flash模拟EEPROM存读取数据的学习.rar.rar (3.74 MB)



学习笔记的部分内容如下:
现在我们看一下怎样应用内部的flash存储/读取数据。我们也要知道stm32内部flash的操作细节。注意在读写的时候开始地址必须是一页的开始地址,不然读写是不行的。

根据《STM32F10X闪存编程》(没有的请在以下链接的网盘中下载:
http://pan.baidu.com/share/link?shareid=3042684922&uk=2886683799)中的介绍,如下图:互联型产品中,我们要使用的是1282K字节/页的主存储空间,也就是说这段空间里除了保存用户代码的部分,其余部分我们是可以利用其作为数据存储使用的。另外,对Flash 的写入操作要“先擦除后写入”的原则。


下面我们解释一下AN2594.pdf(文件下载地址:
http://pan.baidu.com/share/link?shareid=3042684922&uk=2886683799)中模拟EEPROM的机制。官方例程中用了2FLASH空间来作为模拟EEPROM进行数据存储,例如页30x08000C00-0x08000FFF)和页40x08001000-0x080013FF),分别将其标记为PAGE0PAGE1,简单流程如下图

机制如上图所示:如果 0 页空间写满数据,那么把 0页空间里面的【有效数据】复制到 1 页,如果 1页数据满那么把 1 页空间里面的【有效数据】复制到 0 页,这样循环使用,当然如果你想增加使用寿命可以增加多页循环。每页前面 4 字节保留,其中前 2 字节是该页状态标志。


官方文档中的这张图说明了虚拟的EEPROM在FLASH中的保存形式,对页进行以4字节为单位的分块,每块的前2字节保存虚拟EEPROM的16bit数据,后两字节保存此数据的16bit虚拟地址,虚拟地址必须为(0x0000-0xFFFE)。

那么先在这里说一下页面的三种状态


1ERASED   页面是空的或者刚刚擦除数据,此时整个页面都是0xFFFF
2RECEIVE_DATA   按照官方解释是,此页面处在接收已满页面的有效数据过程中。一旦另一页面完成擦除(数据搬运完毕),此页面状态即变成VALID_PAGE。搬运的时候先将最新更新的数据写入,然后再将所有有效数据(除刚刚更新的虚拟地址的数据)写入页面。  状态字:0xEEEE
3VALID_PAGE   页面含有有效数据,这种状态会一直保持,直到所有有效数据搬运到已擦除的页面(有效数据搬运到新页面)  状态字:0x0000


1.写数据
前面已经说到每页前4个字节保留,其中前2字节为页面状态字。假设保存的数据虚拟地址是 0x7777,那么程序写数据是从当前有效页页首地址开始查询虚拟地址位置为0xFFFF的空间,如果是 0xFFFF 那么该位置可以保存数据;如果不是,那么继续找下 1 个位置,如果本页无 0XFFFF 的空间那么表示本页已满,那么将本页【有效数据】复制到另外 1 页继续保存数据。

2.读数据
读数据时是从有效页的末尾地址开始检测是否是有效数据,如果是那么立即返回,程序是通过虚拟地址判断有效数据的,第1 个匹配的虚拟地址的数据才是有效的。

3.对【有效数据】的解释
在两次保存虚拟地址为0x7777的数据时(如下图所示)由于写数据时总是在FLASH中从首至尾依次存放,而读的时候总是从尾至首查找匹配,所以最后一次写入的虚拟地址是 0x7777对应的数据 1245 才是有效的。这就是虚拟数据的更新。


页满时的数据处理
当有新数据要写入而页面内无0xFFFF地址即页面已满时,会将数据写入新的页面,并将原页面的有效数据也复制至新的页面,紧接着擦除已满的页面。如下图所示:


关于优化的问题
STM32 FLASH 模拟EEPROM优化
官方例程中读写数据每次要查询读写位置,写数据是从页首地址开始查询,读地址是从页末地址查询。假如只有 1 个数据,读数据时效率是很低的,要查到最后才能找到有效数据,如果页快满了写数据效率也很低,读效率反而好一点了。实际程序中记录下一个可以写数据的位置将提高数据的读写效率,这样的话:写数据就是立即写不用查询,读数据不从页末地址查询,而是从最后 1 个写入数据处查询,这样特别在页数据少时效率提高不少。优化过的例子代码只需要增加很少部分就能实现。具体的操作请大家自己研究了,这里只对官方提供的例程的使用做以说明,不再展开了。

         了解了前面的flash操作的机制后,下面我们开始代码的学习:
本节用到了RCC、USART、TIM和GPIO,以及printf函数的知识,主要是串口的中断输入输出,这些都在前面的章节讲过了,这里不再赘述。我们主要讲解如何使用下面读写flash的两个函数:
     EE_WriteVariable(地址, 要写入的数据); 写数据
     EE_ReadVariable(地址, 存储数据的数组);读数据
两个函数都已经封装在了 eeprom.c和eeprom.h文件中了。大家在使用的时候直接把文件添加到自己的工程,调用读写函数即可,很方便在自己的工程中实现flash的读写功能。
         我们的例程中保存数据和读取数据的两个函数用的就是这两个函数,值得我们注意的是就是在操作的前后要先解锁flash 然后上锁即可。代码如下:
void  flash_save(u16* CFG)//将传入的数据保存在flash
{
   int count;
   int i;
   count = Uart0_rev_countmap;//备份的接收到的数据的个数

   FLASH_Unlock(); //flash解锁

  /* EEPROM Init */
  EE_Init();
  for (i = 0; i < count; i++)
  {
    EE_WriteVariable(VirtAddVarTab[0]+i,CFG);
  }
  FLASH_Lock();//flash上锁
}
voidflash_read(void) //flash上读取数据到read1[]
{
  int count;
  int i;
  count = Uart0_rev_countmap; //备份的接收到的数据的个数
  FLASH_Unlock();
/*EEPROM Init */
  EE_Init();
  for(i = 0; i < count; i++)
  {
     EE_ReadVariable(VirtAddVarTab[0]+i,&read1);
  }
  FLASH_Lock();
}



下面我们看一下我们程序实现的功能,主循环代码如下:
   if(completeflag=='A')
    {
     USART_SENDS(RxBuffer1,Uart0_rev_countmap);
     completeflag='V'; //將标志位还原为初始状态 V
     flash_save(RxBuffer1);
     flash_read();
     printf("\n\r        读到flash的数据:    \n\r");
     USART_SENDS(read1,Uart0_rev_countmap);
}
当接收到串口接收成功的标志(completeflag=='A')后,先把接收到的数据通过串口打印到电脑上,然后清掉这个接收成功的标志;然后将接收到的数据保存到flash上,再从flash上将数据读取出来,最后把读取出来的数据通过串口打印到电脑上。
串口中断的处理函数如下:
voidUSART1_IRQHandler(void)
{
  if(USART_GetITStatus(USART1, USART_IT_RXNE)!= RESET)
  {   
    TIM_Cmd(TIM2, ENABLE);
    TIM2->CNT &= 0x0000;//定时器延时设置  每次接受清零 当接受间隔超过定时器定的值时  进入tim中断  认为一帧接受完成
    RxBuffer1[RxCounter1++] = USART_ReceiveData(USART1);
    USART_ClearITPendingBit(USART1,USART_IT_RXNE);
  }
}
主要作用就是将串口接收到的数据存储到缓冲区RxBuffer1中,顺便用RxCounter1计数。
程序的完整代码会贴在最后面。
程序完成后,我们将程序烧录到开发板上,连接好USB转串口线,然后打开串口调试助手,在字符串输入框输入“科星 CORX corxstm32.taobao.com”,然后点击发送,就会打印出开发板接收到的数据,和从开发板MCU上的flash中读取出的数据,另外,这里串口接收数据采用中断方式接收,可以连续发送数据,MCU都会进行同样的响应的。如下图所示:

使用特权

评论回复
59
ren0zhe|  楼主 | 2013-10-21 09:47 | 只看该作者
最近比较忙,没来得急更新,以后得更加努力了

使用特权

评论回复
60
ren0zhe|  楼主 | 2013-10-22 16:31 | 只看该作者
第十二节  使用I2C接口读写EEPROM的学习



      这一节我们着重讲硬件 I2C的配置和读写EEPROM的函数,这里使用的是I2C的dma的功能实现的读写操作。使用的EEPROM的芯片型号为AT24C01。


学习笔记的pdf版:
12科星F107开发板学习笔记—使用I2C接口读写EEPROM的学习.pdf (497.85 KB)

源码实例工程文件:
12科星F107开发板学习笔记源码—使用I2C接口读写EEPROM的学习.rar (3.85 MB)




部分内容如下:
        I2C算是MCU比较常用的一个外设接口,我接触到项目,用到I2C的主要就是读写铁电和EEPROM等存储器;还有就是LCD的输出显示。
下面我们先看一下科星F107开发板EEPROM处的硬件原理图:

   我们这里:
科星F107开发板的MCU做主机,开发板的EEPROM芯片做从机。
DMA请求
DMA请求(当被使能时)仅用于数据传输。发送时数据寄存器变空或接收时数据寄存器变满,则产生DMA请求。DMA请求必须在当前字节传输结束之前被响应。当为相应DMA通道设置的数据传输量已经完成时,DMA控制器发送传输结束信号ETO到I2C接口,并且在中断允许时产生一个传输完成中断.
前面的讲述可能有点乱,有点难懂,更加详细的内容,请查阅芯片手册的 I2C章节了,这里不再重复叙述。
下面我们来进行程序的编写:
首先我们打开,第二节中新建的空工程,没有的朋友可以在下面的链接中找到:
下面我们打开这个工程,
第一步,由于我们这里要使用到库文件中串口配置的函数,这里需要添加外设库文件
stm32f10x_dma.cstm32f10x_i2c.cstm32f10x_usart.c,文件的路径为
\Libraries\STM32F10x_StdPeriph_Driver\src”。如下图:
                              
第二步,右击工程文件名,打开“Options”,找到“GerenalOptions”中的“Library Configuration”,在Library选项选择“Full”,如下图:

第三步,添加C语言标准输入输出的头文件#include <stdio.h>,其余的关于printf函数的代码就不多说了,不了解的请看第六节串口输出的内容。
本节用到了RCC、USART和GPIO,以及printf函数的知识,主要是串口的输出那节的内容,前面讲过了,这里不再赘述。
下面我们着重讲一下I2C的配置:
配置函数如下:
voidsEE_Init(void)
{
  I2C_InitTypeDef  I2C_InitStructure;//定义 初始化 I2C的结构体
  
  sEE_LowLevel_Init();//配置SCLSDAGPIO口,配置DMANVIC
  
  /*!< I2C configuration */
  /* sEE_I2C configuration */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//配置I2C的模式,这里选择I2C模式。                     
这里可以有三种模式可供选择:
对应的寄存器为I2C_CR1,寄存器的内容如下:
  I2C_InitStructure.I2C_DutyCycle =I2C_DutyCycle_2;//设定I2C快速模式时的占空比
Specifies the I2C fastmode duty cycle”,更加详细的解释,请看芯片手册的I2C_CCR
寄存器的解释,如下图:
I2C_InitStructure.I2C_OwnAddress1 =I2C_SLAVE_ADDRESS7;// 库的解释为“Specifies the first device own address”,我的理解为这里设定的是 MCU作为从机时的I2C接口地址,当MCU作为主机模式时,它是没有用的,这里我们把这句话注释掉,也是可以正常运行的。I2C_SLAVE_ADDRESS7的宏定义位置如下
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //设置是否开启ACK响应
  I2C_InitStructure.I2C_AcknowledgedAddress =I2C_AcknowledgedAddress_7bit;// Specifies if 7-bit or 10-bit address is acknowledged.”指定应答从地址的位数
I2C_OAR1寄存器中,如下图
I2C_InitStructure.I2C_ClockSpeed= I2C_SPEED;//配置I2C的时钟,大于1,小于400K,这里的取值为100K,定义的地方如下图

  /* sEE_I2C Peripheral Enable */
  I2C_Cmd(sEE_I2C, ENABLE);
  /* Apply sEE_I2C configurationafter enabling it */
  I2C_Init(sEE_I2C, &I2C_InitStructure);
  /* Enable the sEE_I2C peripheral DMA requests */
  I2C_DMACmd(sEE_I2C, ENABLE);
  
#if defined (sEE_M24C64_32)
  /*!< Selectthe EEPROM address according to the state of E0, E1, E2 pins */
  sEEAddress =sEE_HW_ADDRESS;  
#elif defined (sEE_M24C08)
  /*!< dependingon the sEE Address selected in the i2c_ee.h file */
#ifdefsEE_Block0_ADDRESS
  /*!< Selectthe sEE Block0 to write on */
  sEEAddress =sEE_Block0_ADDRESS;
#endif
  
#ifdefsEE_Block1_ADDRESS
  /*!< Selectthe sEE Block1 to write on */
  sEEAddress =sEE_Block1_ADDRESS;
#endif
#ifdefsEE_Block2_ADDRESS
  /*!< Selectthe sEE Block2 to write on */
  sEEAddress =sEE_Block2_ADDRESS;
#endif
  
#ifdefsEE_Block3_ADDRESS
  /*!< Selectthe sEE Block3 to write on */
  sEEAddress =sEE_Block3_ADDRESS;
#endif
上面绿色这部分主要是配置EEPROM的地址,前面宏定义了 sEE_M24C08,如下图。M24C08又包含4个块,这里选用的是“sEE_Block0_ADDRESS”。
#endif/*! < sEE_M24C64_32 */   
}
到这里,所有的配置操作就好了,然后就可以 EEPROM 写入数据和读取数据了,代码如下:
sEE_WriteBuffer(Tx1_Buffer,sEE_WRITE_ADDRESS1, BUFFER_SIZE1);//写入数据
  NumDataRead = BUFFER_SIZE1;//设置读取的数据个数
  sEE_ReadBuffer(Rx1_Buffer, sEE_READ_ADDRESS1,(uint16_t *)(&NumDataRead));//读取数据
函数sEE_WriteBuffer()的参数的含义如下:
Tx1_Buffer”表示存储要被写入EEPROM的数据的缓冲区的首地址
sEE_WRITE_ADDRESS1”表示要写入的EEPROM的内部地址
BUFFER_SIZE1”表示要写入EEPROM的字节数
函数sEE_ReadBuffer()的参数的含义:
Rx1_Buffer表示接收从EEPROM读出来的数据的缓冲区的地址
sEE_READ_ADDRESS1”表示从EEPROM中读取的首地址
(uint16_t *)(&NumDataRead”表示要从EEPROM读出来的数据的字节数
         所以这里的操作就是将Tx1_Buffer中的数据存入到EEPROM中,然后从EEPROM中读取出来,存储到Rx1_Buffer中。最后通过串口把结果打印出来。
程序完成后,将代码烧录到科星F107开发板上,然后连接好串口线,打开串口调试助手,然后就可以查看打印出来的数据了,每次复位开发板,可以打印一次,写入和读出EEPROM中的数据,效果如下图:

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则