最新日志

发表于:2008-8-29 16:14:12
标签:STM32  GPIO  外设管脚  

1

在STM32中如何配置片内外设使用的IO端口

首先,一个外设经过配置输入的时钟和初始化后即被激活(开启)。

如果需要使用该外设的输入输出管脚,则需要配置相应的GPIO端口;否则该外设对应的输入输出管脚可以做普通GPIO管脚使用。


STM32的输入输出管脚有下面8种可能的配置:
  1. 浮空输入
  2. 带上拉输入
  3. 带下拉输入
  4. 模拟输入
  5. 开漏输出
  6. 推挽输出
  7. 复用功能的推挽输出
  8. 复用功能的开漏输出

对应到外设的输入输出功能有下述三种情况:
一、外设对应的管脚为输出:需要根据外围电路的配置选择对应的管脚为复用功能的推挽输出复用功能的开漏输出
二、外设对应的管脚为输入:则根据外围电路的配置可以选择浮空输入带上拉输入带下拉输入
三、ADC对应的管脚:配置管脚为模拟输入


这一点在手册中没有十分明确地说明,我们已经要求写手册的人在下一版本的手册中加入。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(0) | 阅读(58)
发表于:2008-8-29 15:34:49
标签:STM32  GPIO  输入电平  

1

STM32 GPIO端口的输入电平说明

STM32的I/O管脚有两种:TTL和CMOS,所有管脚都兼容TTL和CMOS电平。

也就是说,从输入识别电压上看,所有管脚(不管是TTL管脚还是CMOS管脚)都可以识别TTL或CMOS电平。

下面是摘自STM32数据手册的有关数据:

 Symbol  Parameter  Conditions  Min  Typ  Max  Unit
 VIL Input low level voltage  TTL Ports  -0.5    0.8  V
 VIH Standard IO input high level voltage  2    VDD+0.5
IO FT input high level voltage  2    5.5V
 VIL Input low level voltage  CMOS Ports  -0.5    0.35VDD  V
 VIH Input high level voltage  0.65VDD    VDD+0.5

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(0) | 阅读(66)
发表于:2008-8-29 11:06:38
标签:STM32  USB  非控制端点  多个数据包  

1

如何使用STM32的USB非控制端点发送多个数据包

以下是网友提出的问题和我对这个问题的说明。


SMT32F103,根据例程Custom_HID修改,利用EP1 以EP_INTERRUPT 的方式 发送包,原来的例程每次发送2个字节,现在修改后包的长度不超过64字节时发送是正常的,但当一个包长超过64字节时就发送失败,没有数据出来(程序没有死机),该改的地方都已经修改了,不知道哪个地方还没有改到位,谢谢!

现象就是 超过63字节的包死活也发不出去,而且发送包的大小 还与 CustomHID_ConfigDescriptor里面的 EP1 IN endpoint 描述里包大小有关 ,没道理啊,其他的MCU 这地方设置为8 照样发送256B 以上的包。

在Custom_HID例程上修改了如下代码:
1.usb_proc.c 的CustomHID_Reset()里  SetEPTxCount(ENDP1, 64);
2.关闭 DMA中断,不让ADC采样后发送EP1包
3.在main.c里 重复发送一个128B的包,
   while(1){
    for(i=0;i<2;i++)
       { SetEPTxAddr(ENDP1, ENDP1_TXADDR+i*64);
      SetEPTxValid(ENDP1);    
       Delay(10000);      
        }
     }       
4. 由于一个包是128B,最大包长是64B,所以分两次发送出来,奇怪的是所有例程发送包时都没有查发送状态的处理,也没有找到相应的状态等待函数,这样的话,是不是出现第一个包还没有发送完,第二个包就冲掉了第一个包的数据?
5. 所以问题很简单,就是如何发送一个多数据包,发送函数要如何写?

以下是关于这个问题的解答:


分两次发送是对的,但关键是每次发送前需要检查上次发送是否完成。

检查一个端点的发送是否结束有2种方法,第一种方法是当发送结束(设备收到ACK)时,有一个发送结束中断,这个中断由USB库处理,并通过EP1_IN_Callback这个回调函数交由用户程序确认,你可以搜索一下,例子中把EP1_IN_Callback定义为NOP_Process,没有处理这个回调事件。如果要用这种方法检测端点发送结束,你需要自己定义回调函数并做相应处理。

检测端点发送结束的另一个方法是查询这个端点的状态,如果端点状态处于EP_TX_VALID,说明发送未结束,如果端点状态处于EP_TX_NAK,说明发送结束。使用下述调用可以得到端点1的发送状态:
   GetEPTxStatus(ENDP1)


按照你的思路,可以使用第二种方法实现发送多个数据包的功能。

假定要发送150个字节的MyBuffer,EP1的最大包长设为64字节。

u8 MyBuffer[150];
int packetN;
packetN = 3;
while (1) {
   if (packetN < 3) { // 有数据需要发送时置packetN为'0'
     if (GetEPTxStatus(ENDP1) == EP_TX_NAK) {
       if (packetN == 0) { // 拷贝头64字节到发送缓冲区
         UserToPMABufferCopy(MyBuffer, ENDP1_TXADDR, 64);
         SetEPTxCount(ENDP1, 64);
       }
       else if (packetN == 1) { // 拷贝第2个64字节到发送缓冲区
         UserToPMABufferCopy(MyBuffer+64, ENDP1_TXADDR, 64);
         SetEPTxCount(ENDP1, 64);
       }
       else if (packetN == 2) { // 拷贝最后22字节到发送缓冲区
         UserToPMABufferCopy(MyBuffer+128, ENDP1_TXADDR, 22);
         SetEPTxCount(ENDP1, 22);
       }
       packetN++;
       SetEPTxStatus(ENDP1, EP_TX_VALID);
     }
   }
   ...... // 其它操作
}

这里使用了一个变量记录应该发送第几个数据包,当程序的其它部分准备好数据后只要设置这个变量packetN=0,上述发送操作就会启动,程序的其它部分只需检测packetN==3即可知道MyBuffer是否已经腾空,程序的其它部分可以使用MyBuffer继续其它操作,注意这时数据不一定已经全部发送完毕。


你的另一个问题在于这一行:SetEPTxAddr(ENDP1, ENDP1_TXADDR+i*64);

ENDP1_TXADDR是专门的发送缓冲区,它的长度是有限的,而且是每32位编址中只有低16位有效;所以需要使用函数UserToPMABufferCopy()操作这个发送缓冲区,这个函数已经在USB库的手册中说明。


最后一个问题是:如果你的程序中使用了ENDP1_RXADDR,因为你改变了ENDP1包的长度,即改变了发送缓冲区的长度,需要在usb_conf.h中重新定义以下ENDP1_RXADDR的地址。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(0) | 阅读(48)
发表于:2008-8-28 14:12:28
标签:STM32  运行速度  Flash  

1

【分析】STM32的代码,跑在RAM里快?还是跑在Flash里快?

这肯定是很多人关心的问题,下面通过一个例子看看会有什么样的结论:


测试手段如下:

主循环一直在做一个变量的自加(sum1++),当然前提保证不会溢出。

用Cortex-M3内部的Systick计数,以一秒钟为限,这个sum1的数值大小,可以判断哪种方式比较快。为了严密,我们观察第一秒到第二秒之间的计数效果;而不是从第0秒到第1秒(因为使能Systick到真正开始执行sum1++可能有间隙)。在第一次进入Systick的ISR时,记录下sum1的值;第二次进入Systick的ISR时,再次记录sum1的值,两次值之差即为一秒钟间隔中sum1执行了多少次自加。由此看出哪种方式比较快。

同样的测试前提:Prefetch Buffer Enable + Flash Latenty="2" (根据Flash Programming Manual中要求的那样,当48MHz<SYSCLK<=72MHz时,对flash的访问插入两个等待周期)


测试结果如下:
不对代码优化,在RAM中执行程序:sum1计数69467/秒
不对代码优化,在FLASH中执行程序:sum1计数43274/秒 (Flash里跑得慢)

/***********循环体内代码为N个以下的block*************/
(1)LDR R0,[PC, #0x154]
(2)LDR R1,[PC, #0x154]

(3)LDR R1,[R1,#0]
(4)ADDS R1, R1,#0x1

(5)STR R1,[R0, #0]

    ......
/****************************************************/

打开速度优化开关,在RAM中执行程序:sum1计数98993/秒
打开速度优化开关,在FLASH中执行程序:sum1计数115334/秒 (Flash里跑得快)

/***********循环体内代码为N个以下的block*************/
(1)LDR R1,[R1,#4]
(2)ADDS R1, R1,#0x1
(3)STR R1,[R0, #0]
    ......
/****************************************************/


结论就是:

1)程序运行在RAM里速度快还是运行在Flash里速度快,不是绝对的一概而论的,取决于代码;

2)就以上两种具体的代码情况来说,我觉得无优化时,如果在Flash里执行:(1)(2)的取指(读flash)->译码->执行(读flash);取指和执行阶段flash的目标地址不是连续的,因此是non-sequencial access,所以会很慢;
打开优化时,(1)(2)(3)都不会造成flash的non-sequential access,所以在flash里的优势(取指和取数据走不同的总线ICode和DCode以及Prefetch)就体现出来了。


再进一步的分析,又有这样一些结论:

没有优化时,指令执行时要到Flash中取常数,结果造成指令预取队列的取指中断,取完常数后需要重新填充指令预取队列,而Flash访问需要插入等待周期,当然时间就比较长了。

经过代码优化后,指令执行时不用再到Flash中取常数,指令预取队列不会被打断,而Flash访问需要插入等待周期的效应被下面贴子中介绍的取指缓冲区抵消,所以自然速度就快了;而这个时候在RAM中执行反而慢了是因为RAM不在ICode总线上,从RAM取指需要绕一圈,当然要比在ICode总线上的Flash慢了。

关于Flash的性能,请看我的另一篇分析:【分析】STM32从Flash中运行程序的时序分析


另外,STR9与STM32的总线架构是一样的,这里有一个在STR9上实现的FFT函数的实测数据,可以进一步说明在Flash中运行代码可以比在RAM中快!

在ST的网站上有一个DSP的函数库,这是它的文档《STR91x DSP library (DSPLIB)》,在这篇文档中有一节讨论FFT运算速度的,那里给出了实际的运算时间比较,摘录如下:

Radix-4
Complex FFT
Operation Mode Cycle Count Microseconds
64 Point Program in Flash & Data in SRAM 2701 28.135
64 Point Program & Data in SRAM 3432 35.75
64 Point Program & Data in Flash 3705 38.594
256 Point Program in Flash & Data in SRAM 13740 143.125
256 Point Program & Data in SRAM 18079 188.323
256 Point Program & Data in Flash 19908 207.375

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(0) | 阅读(85)
发表于:2008-8-27 12:03:01
标签:STM32  GPIO  PC14-PC15  复用管脚  

2

STM32中如何使用PC14和PC15

在STM32的数据手册的管脚分配图中可以看到:PC14与OSC32_IN公用一个引脚,PC15与OSC32_OUT公用一个引脚,它们的使用方法如下:

当LSE(低速外部时钟信号)开启时,这两个公用管脚的功能是OSC32_IN和OSC32_OUT。
当LSE(低速外部时钟信号)关闭时这两个公用管脚的功能是PC14和PC15。

备用区域控制寄存器(RCC_BDCR)的LSEON用于控制LSE的开启或关闭。关于这个寄存器的用法请参看《STM3210x技术参考手册》。


作为GPIO输出的配置过程:

(1).使能GPIOC时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
 
(2).配置GPIOC
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

(3).然后写高写低
  GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_SET);
  GPIO_WriteBit(GPIOC, GPIO_Pin_15, Bit_SET);
  GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_RESET);
  GPIO_WriteBit(GPIOC, GPIO_Pin_15, Bit_RESET);

以上代码跑在两个板子上,一个是backup区域里的RCC_DBCR的LSEON为0,即LSE关闭,以上(3)的操作从示波器上看到了电平相应变换;另外一个板子,backup区域里的RCC_DBCR的LSEON为1,即LSE打开,则以上(3)的操作从示波器上看到无效。


作为GPIO输入的配置过程:

只是把以上的(2)稍微改一下,(3)就不用了
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

外部连接为:在PC14引脚焊了一个电阻,电阻另一端通过跳线在上拉到3.3V和下拉到地的两种情况下,读出GPIOC_IDR.14分别为"1"和"0"。


作为外部中断输入的配置过程:

1.时钟使能
// + osc32_in/out --> pc14/15
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC |RCC_APB2Periph_AFIO, ENABLE);
注意要使能AFIO的时钟哦

2.中断配置
  // + for EXTI on PC.14 at falling edge
  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQChannel;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

3. GPIO配置同上
4. EXTI配置
// + for PC14 EXTI @ falling edge
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource14);
  EXTI_InitStructure.EXTI_Line = EXTI_Line14;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);

5. 中断处理ISR
void EXTI15_10_IRQHandler(void)
{
  if(EXTI_GetITStatus(EXTI_Line14) != RESET)
  {
    /* Clear the EXTI line 9 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line14);
  }
}

在中断处设置断点,
只要按下key,就进入中断,跑出中断,再按key,再次进入,屡试不爽,试验完成~~~

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(1) | 阅读(239)
发表于:2008-8-25 15:07:49
标签:STM32  USB  开发库  HID  

1

如何使用STM32的USB库支持控制端点0

首先我们先回顾一下控制端点的传输方式:

控制端点的传输有三个阶段,SETUP阶段、数据阶段和状态阶段;数据阶段又分为数据入(DATA IN)和数据出(DATA OUT),控制端点传输可以没有数据阶段;状态阶段有状态入(STATUS IN)和状态出(STATUS OUT)。

总结起来,控制端点有如下三种可能的传输过程(以下括号中的0或1表示DATA0或DATA1传输):
一、 SETUP  DATA_IN(0)  DATA_IN(1)  DATA_IN(0)  ......  STATUS_OUT(1)
二、 SETUP  DATA_OUT(0)  DATA_OUT(1)  DATA_OUT(0)  ...... STATUS_IN(1)
三、 SETUP  STATUS_IN(1)

这里做一个约定,把上述过程一定义为“数据入过程”,过程二定义为“数据出过程”,过程三定义为“无数据过程”。所有的USB控制端点的数据传输都可以而且只用这三种传输过程表示。HID的SET_REPORT是数据出过程,HID的GET_REPORT是数据入过程,USB的GET DEVICE DESCRIPTOR是数据入过程,USB的SET CONFIGURATION是无数据过程,等等。


接下来,我们看看STM32的USB库是如何处理控制端点0的传输。

根据USB协议,每个SETUP包都由8个字节构成,用户程序可以通过结构体Device_Info(类型DEVICE_INFO)访问SETUP包的数据,因为在整个的USB处理中都要用到结构体Device_Info的内容,库中定义了一个全局的指针pInformation指向这个结构体,用户可以通过这个指针访问结构体的内容。

对应SETUP包的8个字节,用户可以用下述方式访问:
  pInformation->USBbmRequestType (字节类型)
  pInformation->USBbRequest  (字节类型)
  pInformation->USBwValue  (双字节类型)
  pInformation->USBwIndex  (双字节类型)
  pInformation->USBwLength (双字节类型)
使用pInformation->USBwValue0访问wValue的低字节,pInformation->USBwValue1访问wValue的高字节。
使用pInformation->USBwIndex0访问USBwIndex的低字节,pInformation->USBwIndex1访问USBwIndex的高字节。
使用pInformation->USBwLength0访问USBwLength的低字节,pInformation->USBwLength1访问USBwLength的高字节。

通过分析SETUP包的8个字节,可以判断出一个SETUP的传输过程是属于数据入过程数据出过程还是无数据过程。STM32的USB库中处理了所有的USB协议文本中定义的标准SETUP命令,对于USB协议文本中未定义的命令,USB库按照数据入过程数据出过程无数据过程通过回调函数交给用户程序处理。

全局变量Device_Property(DEVICE_PROP类型)封装了所有的回调函数,DEVICE_PROP定义如下:
typedef struct _DEVICE_PROP
{
  void (*Init)(void);        // 设备初始化回调函数
  void (*Reset)(void);       // USB复位回调函数

  void (*Process_Status_IN)(void);  // STATUS_IN阶段处理回调函数
  void (*Process_Status_OUT)(void); // STATUS_OUT阶段处理回调函数

  RESULT (*Class_Data_Setup)(u8 RequestNo);   // 数据入/出过程处理回调函数
  RESULT (*Class_NoData_Setup)(u8 RequestNo); // 无数据过程处理回调函数

  RESULT  (*Class_Get_Interface_Setting)(u8 Interface, u8 AlternateSetting); // GET_INTERFACE 回调函数

  u8* (*GetDeviceDescriptor)(u16 Length); // GET_DEVICE_DESCRIPTION回调函数
  u8* (*GetConfigDescriptor)(u16 Length); // GET_CONFIGURATION_DESCRIPTION回调函数
  u8* (*GetStringDescriptor)(u16 Length); // GET_STRING_DESCRIPTION回调函数
  u8 MaxPacketSize; // 最大包长度
} DEVICE_PROP;

结合SETUP的三种传输过程,用户通过实现不同的回调函数即可完成对各种USB类命令的处理,下面以HID的SET REPORT为例说明。


在介绍具体实现之前,先介绍一下另一个回调函数CopyRoutine的概念,这个函数的原型是:
   u8 *CopyRoutine(u16 length);    // 返回一个缓冲区指针

USB库通过这个函数获得用户的数据缓冲区地址,从而可以在数据出过程中把收到的数据拷贝到用户缓冲区,或在数据入过程中把用户缓冲区的数据拷贝到USB发送缓冲区。每个数据出过程可能有若干次DATA_OUT传输,USB库每完成一次这样的传输都会调用一次回调函数CopyRoutine,参数length是本次传输所收到的数据字节数目,CopyRoutine必须返回一个缓冲区指针,这个缓冲区必须能够容纳length字节的数据,CopyRoutine返回到USB库之后,USB库将把收到的数据拷贝到用户指定的缓冲区。同样每个数据入过程也可能有若干次DATA_IN传输,每次需要向主机传输数据时,USB库都会调用一次回调函数CopyRoutine,参数length是本次传输所要发送的数据字节数目,CopyRoutine必须返回一个缓冲区指针,这个缓冲区中必须包含要求的数据字节,USB库将把用户缓冲区的数据拷贝到USB缓冲区并择机发送出去。

当以length=0调用CopyRoutine时,CopyRoutine需要返回用户缓冲区的长度,因为CopyRoutine的返回类型是一个指针,所以需要通过类型的强制转换返回缓冲区长度。这个功能是为了处理用户缓冲区的长度与主机SETUP数据请求长度不符的情况,而不至于造成用户缓冲区的溢出。


介绍完上述若干概念和回调函数,再看SET_REPORT的实现就很容易了。

SET_REPORT是一个数据出过程,因此需要实现一个Class_Data_Setup回调函数,示例如下:

RESULT HID_Data_Setup(u8 RequestNo)
{
    u8 *(*CopyRoutine)(u16 length);
    CopyRoutine = NULL;
    if (pInformation->USBbmRequestType == CLASS_REQUEST|INTERFACE_RECIPIENT
            && RequestNo == SET_REPORT)
        CopyRoutine = My_Data_Request;

    if (CopyRoutine == NULL)
        return USB_UNSUPPORT;

    pInformation->Ctrl_Info.CopyData = CopyRoutine;
    pInformation->Ctrl_Info.Usb_wOffset = 0;
    pInformation->Usb_wLength = (*CopyRoutine)(0);

    return USB_SUCCESS;
} // End of HID_Data_Setup()

u8 My_Buffer[10];
u8 *My_Data_Request(u16 length)
{
    if (length == 0)
        return (u8*)10;    // 假定你的REPORT长度和Buffer长度为10

    return My_Buffer;
}


上面介绍的CopyRoutine用于把多次传输的数据包合并到一个完整的缓冲区中,因此只有到STATUS阶段才能够知道一次SETUP传输是否结束,所以用户程序需要在回调函数Process_Status_IN中处理从SET_REPORT接收到的数据。因为所有的回调函数都是USB中断处理的一部分,所以更好的办法是在Process_Status_IN中设置一个标记,然后在用户主程序中判断这个标记并做处理。


上述示意代码是以My_Buffer长度为10字节为例,而USB库的默认包长度为16字节,因此My_Data_Request并没有多包的处理。

关于多包的缓冲区处理的示意代码可以是这样的:

u8 *My_Data_Request(u16 length)
{
    if (length == 0)
        return (u8*)100;    // 假定你的REPORT长度和Buffer长度为100

    return &My_Buffer[pInformation->Ctrl_Info.Usb_wOffset];
}

这里有一个库中使用的变量pInformation->Ctrl_Info.Usb_wOffset,这个变量会在传输每个数据包时候由库中的程序按数据包长度增加,如最大包长为16字节时,第一次调用My_Data_Request时Usb_wOffset=0,第二次调用My_Data_Request时Usb_wOffset=16,第三次调用My_Data_Request时Usb_wOffset=32,依此类推。这样就可以使用Usb_wOffset作为My_Buffer的下标从My_Data_Request返回相应的缓冲区地址。


前面已经说明,参数length是用于检测缓冲区长度是否足够,如果你有足够长的缓冲区,可以不必检测,上述示例中使用了一个固定的缓冲区,所以不必使用参数length检测缓冲区长度。


注意,STM32的USB库设计成以回调函数处理用户命令请求,包含类命令请求,是为了能够清晰地区分库程序和用户程序,使这两者不会混在一起,这样的好处是非常明显的,当USB库需要更新升级时,只需替换掉相应的程序模块,而不必修改用户已经完成的程序。

以上的介绍都可以在STM32 USB库的说明手册中找到。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(2) | 阅读(192)
发表于:2008-8-6 11:35:17
标签:STM32  Cortex-M3  NVIC  库函数  

5

STM32(Cortex-M3)中的优先级概念

STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

所有8位用于指定响应优先级
最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。


Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组
NVIC_PriorityGroup_1 => 选择第1组
NVIC_PriorityGroup_2 => 选择第2组
NVIC_PriorityGroup_3 => 选择第3组
NVIC_PriorityGroup_4 => 选择第4组

接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
 
// 使能EXTI0中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
 
// 使能EXTI9_5中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);




要注意的几点是:

1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(4) | 阅读(384)
发表于:2008-7-30 10:30:22
标签:STM32  IAP  读保护  

2

什么是IAP?如何实现IAP?

IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。

通常在用户需要实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信管道(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在User Flash中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:

1)检查是否需要对第二部分代码进行更新
2)如果不需要更新则转到4)
3)执行更新操作
4)跳转到第二部分代码执行

第一部分代码必须通过其它手段,如JTAG或ISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一道烧入,以后需要程序更新是再通过第一部分IAP代码更新。

对于STM32来说,因为它的中断向量表位于程序存储器的最低地址区,为了使第一部分代码能够正确地响应中断,通常会安排第一部分代码处于Flash的开始区域,而第二部分代码紧随其后。

在第二部分代码开始执行时,首先需要把CPU的中断向量表映像到自己的向量表,然后再执行其他的操作。


如果IAP程序被破坏,产品必须返厂才能重新烧写程序,这是很麻烦并且非常耗费时间和金钱的。针对这样的需求,STM32在对Flash区域实行读保护的同时,自动地对用户Flash区的开始4页设置为写保护,这样可以有效地保证IAP程序(第一部分代码)区域不会被意外地破坏。


ST的网站上有使用USART实现IAP的例子,各位可以参考这个例子和相应的手册。

应用笔记 AN2557 - STM32F10xxx in-application programming using the USART
例程:AN2557 - STM32F10xxx in-application programming using the USART

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(1) | 阅读(277)
发表于:2008-7-28 12:23:34
标签:STM32  NVIC  SysTick  ARM  Cortex-M3  

1

STM32中用到的Cortex-M3寄存器说明

在STM32中用到了Cortex-M3定义的三组寄存器,有关这三组寄存器的说明不在STM32的技术手册中,需要参考ARM公司发布的Cortex-M3 Technical Reference Manual (r2p0)

在STM32的固件库中定义了三个结构体与这三个寄存器组相对应,这三个结构体与ARM手册中寄存器的对应关系如下:

 

一、NVIC寄存器组

STM32的固件库中有如下定义:

typedef struct
{
  vu32 ISER[2];
  u32 RESERVED0[30];
  vu32 ICER[2];
  u32 RSERVED1[30];
  vu32 ISPR[2];
  u32 RESERVED2[30];
  vu32 ICPR[2];
  u32 RESERVED3[30];
  vu32 IABR[2];
  u32 RESERVED4[62];
  vu32 IPR[11];
} NVIC_TypeDef;

它们对应ARM手册中的名称为
ISER = Interrupt Set-Enable Registers
ICER = Interrupt Clear-Enable Registers
ISPR = Interrupt Set-Pending Register
ICPR = Interrupt Clear-Pending Register
IABR = Active Bit Register
IPR = Interrupt Priority Registers

每个寄存器有240位,以Interrupt Set-Enable Registers说明,ISER[0]对应中断源0~31,ISER[1]对应中断源32~63,STM32只有60个中断源,所以没有ISER[2:7]。

参考STM32技术参考手册中的中断向量表,中断源的位置为:
位置0 - WWDG = Window Watchdog interrupt
位置1 - PVD = PVD through EXTI Line detection interrupt
位置2 - TAMPER = Tamper interrupt
......
位置58 - DMA2_Channel3 = DMA2 Channel3 global interrupt
位置59 - DMA2_Channel4_5 = DMA2 Channel4 and DMA2 Channel5 global interrupts

 

二、系统控制寄存器组

STM32的固件库中有如下定义:

typedef struct
{
  vuc32 CPUID;
  vu32 ICSR;
  vu32 VTOR;
  vu32 AIRCR;
  vu32 SCR;
  vu32 CCR;
  vu32 SHPR[3];
  vu32 SHCSR;
  vu32 CFSR;
  vu32 HFSR;
  vu32 DFSR;
  vu32 MMFAR;
  vu32 BFAR;
  vu32 AFSR;
} SCB_TypeDef; /* System Control Block Structure */

它们对应ARM手册中的名称为

CPUID = CPUID Base Register
ICSR = Interrupt Control State Register
VTOR = Vector Table Offset Register
AIRCR = Application Interrupt/Reset Control Register
SCR = System Control Register
CCR = Configuration Control Register
SHPR = System Handlers Priority Register
SHCSR = System Handler Control and State Register
CFSR = Configurable Fault Status Registers
HFSR = Hard Fault Status Register
DFSR = Debug Fault Status Register
MMFAR = Mem Manage Address Register
BFAR = Bus Fault Address Register
AFSR = Auxiliary Fault Status Register

 

三、系统时钟寄存器组

STM32的固件库中有如下定义:

typedef struct
{
  vu32 CTRL;
  vu32 LOAD;
  vu32 VAL;
  vuc32 CALIB;
} SysTick_TypeDef;

它们对应ARM手册中的名称为

CTRL = SysTick Control and Status Register
LOAD = SysTick Reload Value Register
VAL = SysTick Current Value Register
CALIB = SysTick Calibration Value Register

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 整理

评论(2) | 阅读(395)
发表于:2008-7-16 21:33:31
标签:STM32  质量  可靠性  电磁兼容性  

1

STM32质量报告和可靠性报告

在ST网站可以下载这些报告:http://www.st.com/mcu/familiesdocs-110.html#Quality%20-%20Product%20Documentation

在STM32数据手册中有有关电磁兼容性和ESD的参数。

点击此处查看原文 >>

系统分类: 单片机   |    用户分类:    |    来源: 原创

评论(0) | 阅读(302)
234下一页总共 , 当前 /