EDN首页   博客首页

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的地址。
系统分类: 单片机
用户分类: 实用例程
标签: STM32 USB 非控制端点 多个数据包
来源: 原创
发表评论 阅读全文(125) | 回复(0)

3

关于投票
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,再次进入,屡试不爽,试验完成~~~

系统分类: 单片机
用户分类: 实用例程
标签: STM32 GPIO PC14-PC15 复用管脚
来源: 原创
发表评论 阅读全文(343) | 回复(1)

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

系统分类: 单片机
用户分类: 实用例程
标签: STM32 IAP 读保护
来源: 原创
发表评论 阅读全文(407) | 回复(1)

1

关于投票
如何使用STM32的PVD对电源的电压进行监控

用户在使用STM32时,可以利用其内部的PVD对VDD的电压进行监控,通过电源控制寄存器(PWR_CR)中的PLS[2:0]位来设定监控的电压值。

PLS[2:0]位用于选择PVD监控电源的电压阀值:
000:2.2V
001:2.3V
010:2.4V
011:2.5V
100:2.6V
101:2.7V
110:2.8V
111:2.9V

在电源控制/状态寄存器(PWR_CSR)中的PVDO标志用来表明VDD是高于还是低于PVD设定的电压阀值。该事件连接到外部中断的第16线,如果该中断在外部中断寄存器中被使能的,该事件就会产生中断。当VDD下降到PVD阀值以下和(或)当VDD上升到PVD阀值之上时,根据外部中断第16线的上升/下降边沿触发设置,就会产生PVD中断。这一特性可用于发现电压出现异常时,执行紧急关闭任务。

点击下载

系统分类: 单片机
用户分类: 实用例程
标签: STM32 PVD 监控 紧急事件
来源: 原创
发表评论 阅读全文(160) | 回复(2)

1

关于投票
运行于EK-STM32F学习板上的STM32固件库中的例子

我们这里一个上海交大来的实习生,在他学习STM32的过程中,把STM32固件库中的所有例子程序移植到了南京万利推出的EK-STM32F学习板,下面是他的工作成果,供各位网友学习参考。

注:因为原有的例子程序是运行于ST标准的评估板——STM3210B-EVAL,有些资源是EK-STM32F学习板没有的,所以部分例子程序没有经过验证。每个例子目录中都有一个简短的说明,标明了修改过的部分,并指明例子是否经过验证。


请先解压这两个文件的内容到同一个目录下,再根据“使用方法.txt”中的步骤操作。文件“stm32lib_contents.htm”中有所有例子的列表。

点击下载STM32F10xFWLib_on_EKSTM32F Part1.zip

点击下载STM32F10xFWLib_on_EKSTM32F Part2.zip

 

系统分类: 单片机
用户分类: 实用例程
标签: STM32 固件库 EK-STM32F开发板
来源: 原创
发表评论 阅读全文(1322) | 回复(3)

1

关于投票
使用STM32的定时器进行输入脉冲的计数

    STM32的定时器具有计数功能,在实际应用中可以用来对引脚上的输入信号进行统计。其输入信号作为计数时钟,输入引脚为ETR引脚。

    本例程使用Timer 2,其ETR输入引脚为PA1,初始化是设置该引脚工作模式为输入模式,Timer2的工作模式为从模式。

    为了方便测试,另外使用PC6模式输出一个时钟信号。测试时将PC6与PA1短接。(用户也可另外连接一个时钟信号到PA1引脚上。)

代码如下:
int main(void)
{
 unsigned char i_Loop;
 unsigned char n_Counter;

#ifdef DEBUG
  debug();
#endif

 RCC_Configuration();  // System Clocks Configuration
 NVIC_Configuration();  // NVIC configuration
 GPIO_Configuration();  // Configure the GPIO ports

 TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
 TIM_TimeBaseStructure.TIM_Prescaler = 0x00;
 TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  // Time base configuration

 TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);

 TIM_SetCounter(TIM2, 0);
 TIM_Cmd(TIM2, ENABLE);

 for(i_Loop = 0; i_Loop < 100; i_Loop ++) {
  GPIO_SetBits(GPIOC, GPIO_Pin_6);
  Delay(10);
  GPIO_ResetBits(GPIOC, GPIO_Pin_6);
  Delay(10);
 }

 n_Counter = TIM_GetCounter(TIM2);
 while (1) {
 }
}

前三行进行了时钟、中断、和I/O口的配置。然后进行Timer的基本配置,计数器自动装载值为0xFFFF,计数频率不分频,定时器时钟(CK_INT)频率与数字滤波器(ETR,TIx)使用的采样频率之间的分频比为1,计数器向上计数。

TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);

使用ETR时钟作为计数时钟需要设置为外部时钟模式2,故调用该函数,在设置时
1、关闭预分频:TIM_ExtTRGPSC_OFF。
 可选项:
    TIM_ExtTRGPSC_OFF      0x0000
    TIM_ExtTRGPSC_DIV2     0x1000
    TIM_ExtTRGPSC_DIV4     0x2000
    TIM_ExtTRGPSC_DIV8     0x3000

2、外部触发极性ETR不反相,高电平或上升沿有效:TIM_ExtTRGPolarity_NonInverted。
 可选项:
    TIM_ExtTRGPolarity_Inverted和
    TIM_ExtTRGPolarity_NonInverted。

3、外部触发无滤波器:0000。
   可选项:
 0:无滤波器,以fDTS采样
 1:采样频率fSAMPLING=fCK_INT,N=2
 2:采样频率fSAMPLING=fCK_INT,N=4
 3:采样频率fSAMPLING=fCK_INT,N=8
 4:采样频率fSAMPLING=fDTS/2,N=6
 5:采样频率fSAMPLING=fDTS/2,N=8
 6:采样频率fSAMPLING=fDTS/4,N=6
 7:采样频率fSAMPLING=fDTS/4,N=8
 8:采样频率fSAMPLING=fDTS/8,N=6
 9:采样频率fSAMPLING=fDTS