"); //-->
豆皮 - STM32开发板入门教程(四) - 串口通讯 UART (原创) 版权所有 STMFANS 原创,转载请保留出处 http://www.stmfans.com/bbs/viewthread.php?tid=1084&extra=page%3D1&frombbs=1 |
一步一步的走 GPIO 按键 LED 定时器都说了 下面开始串口UART咯 本教程的主角是:串口 UART |
通用同步异步收发器(USART)提供了一种灵活的方法来与使用工业标准NR 异步串行数据格式的外部设备之间进行全双工数据交换。 USART利用分数波特率发生器提供宽范围的波特率选择。 它支持同步单向通信和半双工单线通信。它也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。用于多缓冲器配置的DMA方式,可以实现高速数据通信。 主要特性: 全双工的,异步通信 NR 标准格式 分数波特率发生器系统 -发送和接收共用的可编程波特率,最高到4.5Mbits/s 可编程数据字长度(8位或9位) 可配置的停止位 -支持1或2个停止位 LIN主发送同步断开符的能力以及LIN从检测断开符的能力 - 当USART硬件配置成LIN时,生成13位断开符;检测10/11位断开符 发送方为同步传输提供时钟 IRDA SIR 编码器解码器 - 在正常模式下支持3/16位的持续时间 智能卡模拟功能 - 智能卡接口支持ISO7816 -3标准里定义的异步协议智能卡 - 智能卡用到的0.5和1.5个停止位 单线半双工通信 使用DMA的可配置的多缓冲器通信 - 在保留的SRAM里利用集中式DMA缓冲接收/发送字节 单独的发送器和接收器使能位 检测标志 - 接收缓冲器满 - 发送缓冲器空 - 传输结束标志 校验控制 - 发送校验位 - 对接收数据进行校验 四个错误检测标志 - 溢出错误 - 噪音错误 - 帧错误 - 校验错误 10个带标志的中断源 - CTS改变 - LIN断开符检测 - 发送数据寄存器 - 发送完成 - 接收数据寄存器 - 检测到总线为空 - 溢出错误 - 帧错误 - 噪音错误 - 校验错误 多处理器通信 - - 如果地址不匹配,则进入静默模式 从静默模式中唤醒(通过空闲总线检测或地址标志检测) 两种唤醒接收器的方式 - 地址位(MSB) - 空闲总线 |
STM32的串口配置 也挺方便的 首先是配置UART的GPIO口 /******************************************************************************* * Function Name : UART1_GPIO_Configuration * Description : Configures the uart1 GPIO ports. * Input : None * Output : None * Return : None *******************************************************************************/ void UART1_GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; // Configure USART1_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 USART1_Rx as input floating GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); } 然后是配置串口参数 /* 如果使用查询的方式发送和接收数据 则不需要使用串口的中断 如果需要使用中断的方式发送和接收数据 则需要使能串口中断 函数原形 void USART_ITConfig(USART_TypeDef* USARTx, u16 USART_IT, FunctionalState NewState) 功能描述 使能或者失能指定的 USART 中断 USART_IT 描述 USART_IT_PE 奇偶错误中断 USART_IT_TXE 发送中断 USART_IT_TC 传输完成中断 USART_IT_RXNE 接收中断 USART_IT_IDLE 空闲总线中断 USART_IT_LBD LIN中断检测中断 USART_IT_CTS CTS中断 USART_IT_ERR 错误中断 */ /******************************************************************************* * Function Name : UART1_Configuration * Description : Configures the uart1 * Input : None * Output : None * Return : None *******************************************************************************/ void UART1_Configuration(void) { USART_InitTypeDef USART_InitStructure; /* USART1 configured as follow: - BaudRate = 9600 baud - Word Length = 8 Bits - One Stop Bit - No parity - Hardware flow control disabled (RTS and CTS signals) - Receive and transmit enabled */ 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 the USART1*/ USART_Init(USART1, &USART_InitStructure); /* Enable USART1 Receive and Transmit interrupts */ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); /* Enable the USART1 */ USART_Cmd(USART1, ENABLE); } |
发送一个字符 [/******************************************************************************* * Function Name : Uart1_PutChar * Description : printf a char to the uart. * Input : None * Output : None * Return : None *******************************************************************************/ u8 Uart1_PutChar(u8 ch) { /* Write a character to the USART */ USART_SendData(USART1, (u8) ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) { } return ch; } |
发送一个字符串 /******************************************************************************* * Function Name : Uart1_PutString * Description : print a string to the uart1 * Input : buf为发送数据的地址 , len为发送字符的个数 * Output : None * Return : None *******************************************************************************/ void Uart1_PutString(u8* buf , u8 len) { for(u8 i="0";i<len;i++) { Uart1_PutChar(*buf++); } } |
如果UART使用中断发送数据 则需要修改stm32f10x_it.c 中的串口中断函数 并且需要修改void NVIC_Configuration(void)函数 在中断里面的处理 原则上是需要简短和高效 下面的流程是 如果接收到255个字符或者接收到回车符 则关闭中断 并且把标志位UartHaveData 置1 /******************************************************************************* * Function Name : USART1_IRQHandler * Description : This function handles USART1 global interrupt request. * Input : None * Output : None * Return : None *******************************************************************************/ void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { /* Read one byte from the receive data register */ RxBuffer[ RxCounter ] = USART_ReceiveData(USART1); if( RxCounter == 0xfe || '\r' == RxBuffer[ RxCounter ] ) { /* Disable the USART1 Receive interrupt */ USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); RxBuffer[ RxCounter ] = '\0'; UartHaveData = 1; } RxCounter++; } } 修改NVIC_Configuration函数 /******************************************************************************* * Function Name : NVIC_Configuration * Description : Configures NVIC and Vector Table base location. * Input : None * Output : None * Return : None *******************************************************************************/ void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; #ifdef VECT_TAB_RAM /* Set the Vector Table base location at 0x20000000 */ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif /* Configure the NVIC Preemption Priority Bits */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); /* Enable the USART1 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } |
至此 串口就可以工作起来了 附件中的程序功能是 开机后 从串口中输出2行信息 然后就等待接收串口数据 并且把接收到的数据发回到PC机上来 附件有2个 一个是查询方式的 一个是中断方式的 |
豆皮 - STM32开发板基础教程(七) - ADC with DMA(原创) 版权所有 STMFANS 原创,转载请保留出处
|
下面来讲一下STM32的ADC应用。 先闲扯一点其他事情,是我自己的理解。 STM32的优点在哪里? 除去宣传环节,细细分析。 STM32时钟不算快,72MHZ, 也不能扩展大容量的RAM FLASH, 同样没有DSP那样强大的指令集。 它的优势在哪里呢? ---就在快速采集数据,快速处理上。 ARM的特点就是方便。 这个快速采集,高性能的ADC就是一个很好的体现, 12位精度,最快1uS的转换速度,通常具备2个以上独立的ADC控制器, 这意味着, STM32可以同时对多个模拟量进行快速采集, 这个特性不是一般的MCU具有的。 以上高性能的ADC,配合相对比较块的指令集和一些特色的算法支持, 就构成了STM32在电机控制上的强大特性。 好了,正题,怎末做一个简单的ADC,注意是简单的, ADC是个复杂的问题,涉及硬件设计,电源质量,参考电压,信号预处理等等问题。 我们只就如何在MCU内完成一次ADC作讨论。 谈到ADC,我们还要第一次引入另外一个重要的设备DMA. DMA是什么东西呢。 通常在8位单片机时代,很少有这个概念。 在外置资源越来越多以后, 我们把一个MCU内部分为 主处理器 和 外设两个部分。 主处理器当然是执行我们指令的主要部分, 外设则是 串口 I2C ADC 等等用来实现特定功能的设备 回忆一下,8位时代,我们的主处理器最常干的事情是什么? 逻辑判断?不是。那才几个指令 计算算法?不是。大部分时候算法都很简单。 事实上,主处理器就是作个搬运工, 把USART的数据接收下来,存起来 把ADC的数据接收下来,存起来 把要发送的数据,存起来,一个个的往USART里放。 ………… 为了解决这个矛盾, 人们想到一个办法,让外设和内存间建立一个通道, 在主处理器允许下, 让外设和内存直接 读写,这样就释放了主处理器, 这个东西就是DMA。 打个比方: 一个MCU是个公司。 老板就是主处理器 员工是外设 仓库就是内存 从前 仓库的东西都是老板管的。 员工需要原料工作,就一个个报给老板,老板去仓库里一个一个拿。 员工作好的东西,一个个给老板,老板一个个放进仓库里。 老板很累,虽然老板是超人,也受不了越来越多的员工和单子。 最后老板雇了一个仓库保管员,它就是DMA 他专门负责 入库和出库, 只需要把出库 和入库计划给老板过目 老板说OK,就不管了。 后面的入库和出库过程, 员工只需要和这个仓库保管员打交道就可以了。 --------闲话,马七时常想,让设备与设备之间开DMA,岂不更牛X 比喻完成。 ADC是个高速设备,前面提到。 而且ADC采集到的数据是不能直接用的。即使你再小心的设计外围电路,测的离谱的数据总会出现。 那么通常来说,是采集一批数据,然后进行处理,这个过程就是软件滤波。 DMA用到这里就很合适。让ADC高速采集,把数据填充到RAM中,填充一定数量,比如32个,64个 MCU再来使用。 -----多一句,也可以说,单次ADC毫无意义。 |
下面我们来具体介绍,如何使用DMA来进行ADC操作。 初始化函数包括两部分,DMA初始化和 ADC初始化 我们有多个管理员--DMA 一个管理员当然不止管一个DMA操作。所以DMA有多个Channel //ADC with DMA Init #define ADC_Channel ADC_Channel0 #define ADC1_DR_Address ((u32)0x4001244C) void ADCWithDMAInit() { //DMA init; Using DMA channel 1 DMA_DeInit(DMA1_Channel1); //开启DMA1的第一通道 DMA_InitStruct.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA对应的外设基地址,这个地址走Datasheet查 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //转换结果的数据大小 DMA_InitStruct.DMA_MemoryBaseAddr = (unsigned long)&ADC_ConvertedValue; // DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA的转换模式是SRC模式,就是从外设向内存中搬运, DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //M2M模式禁止,memory to memory,这里暂时用不上,以后介 绍 DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA搬运的数据尺寸,注意ADC是12位的, HalfWord就是16位 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Disable; //接收一次数据后,目标内存地址是否后移--重 要概念,用来采集多个数据的 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //接收一次数据后,设备地址是否后移 DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //转换模式,循环缓存模式,常用,M2M果果开启了,这个模式失效 。 DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA优先级,高 DMA_InitStruct.DMA_BufferSize = 1; //DMA缓存大小,1个 DMA_Init(DMA1_Channel1,&DMA_InitStruct); // Enable DMA1 DMA_Cmd(DMA1_Channel1, ENABLE); } void ADCx_Init(unsigned char ADC_Channel) { ADC_DeInit(ADC1); //开启ADC1 ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; //转换模式,为独立转换。转换模式太多了,以后深究 ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //对齐方式,ADC结果是12位的,显然有个对齐左边还是右边 的问题。一般是右对齐 ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; //连续转换模式开启 ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC外部出发开关,关闭 ADC_InitStruct.ADC_NbrOfChannel = 2; //开启通道数,2个 ADC_InitStruct.ADC_ScanConvMode = ENABLE; //扫描转换模式开启 ADC_Init(ADC1, &ADC_InitStruct); ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5); //规则组通道设置,关键函数 转 换器ADC1,选择哪个通道channel,规则采样顺序,1到16,以后解释详细含义,最后一个参数是转换时间,越长越准越稳定 // ADC1 to DMA, Enable ADC_DMACmd(ADC1, ENABLE); //ADC命令,和DMA关联。 //ADC1 Enable ADC_Cmd(ADC1,ENABLE); //开启ADC1 //Reset the Calibration of ADC1 ADC_ResetCalibration(ADC1); //重置校准 //wait until the Calibration's finish while(ADC_GetResetCalibrationStatus(ADC1)) //等待重置校准完成 ; ADC_StartCalibration(ADC1); //开始校准 while(ADC_GetCalibrationStatus(ADC1)) //等待校准完成 ; ADC_SoftwareStartConvCmd(ADC1, ENABLE); //连续转换开始,从选择开始,MCU可以不用管了,ADC将通过DMA不断刷新 制定RAM区 // Attach them; } 最后讲讲滤波算法 滤波的方法以后会开个专题。 特别提一下---没有完美的滤波算法,只有合适的滤波算法。 需要综合考虑信号特点,噪声特点,控制对象等等, 这里用个最简单的滤波算法,均值滤波。 采样16次,取平均值,吼吼,在豆皮上跳动还是蛮小的,合适,吼吼 //16ms finish a ADC detection // return mv unsigned int ADC_filter(void) { unsigned int result="0"; unsigned char i; for(i=16;i>0;i--) { Delay_xms(1); result += ADC_ConvertedValue; } return (unsigned int)(((unsigned long)(result>>4))*3300>>12); } 完整工程文件,IAR520 JLink 豆皮, ![]() |
豆皮 - STM32开发板入门教程(十八) - 工业现场总线 CAN (原创) 版权所有 STMFANS 原创,转载请保留出处
http://www.stmfans.com/bbs/viewthread.php?tid=3046&extra=page%3D1 |
1、首先通读手册中关于CAN的文档,必须精读。 STM32F10xxx 参考手册Rev7V3.pdf 需要精读的部分为 RCC 和 CAN 两个章节。 为什么需要精读 RCC 呢?因为我们将学习 CAN 的波特率的设置,将要使用到 RCC 部分的设置,因此推荐大家先复习下这部分中的几个时钟。 关于STM32的can总线简单介绍 bxCAN是基本扩展CAN(Basic Extended CAN)的缩写,它支持CAN协议2.0A和2.0B。它的设计目标是,以最小的CPU负荷来高效处理大量收到的报文。它也支持报文发送的优先级要求(优先级特性可软件配置)。 对于安全紧要的应用,bxCAN提供所有支持时间触发通信模式所需的硬件功能。 主要特点 ·支持CAN协议2.0A和2.0B主动模式 · 波特率最高可达1兆位/秒 ·支持时间触发通信功能 发送 ·3个发送邮箱 · 发送报文的优先级特性可软件配置 ·记录发送SOF时刻的时间戳 接收 · 3级深度的2个接收FIFO ·14个位宽可变的过滤器组-由整个CAN共享 · 标识符列表 ·FIFO溢出处理方式可配置 ·记录接收SOF时刻的时间戳 可支持时间触发通信模式 ·禁止自动重传模式 ·16位自由运行定时器 ·定时器分辨率可配置 ·可在最后2个数据字节发送时间戳 管理 ·中断可屏蔽 · 邮箱占用单独1块地址空间,便于提高软件效率 |
2、STM32FVBT6 的 can 的工作模式分为 /* CAN operating mode */ #define CAN_Mode_Normal ((u8)0x00) /* normal mode */ #define CAN_Mode_LoopBack ((u8)0x01) /* loopback mode */ #define CAN_Mode_Silent ((u8)0x02) /* silent mode */ #define CAN_Mode_Silent_LoopBack ((u8)0x03) /* loopback combined with silent mode */ 在此章我们的豆皮教程中我们将使用到 CAN_Mode_LoopBack 和 CAN_Mode_Normal 两种模式。 我们第一步做的就是使用运行在 CAN_Mode_LoopBack 下进行自测试。 在参考手册中 CAN_Mode_LoopBack (环回模式) 的定义如下: 环回模式可用于自测试。为了避免外部的影响,在环回模式下CAN内核忽略确认错误(在数据/远程帧的确认位时刻,不检测是否有显性位)。在环回模式下,bxCAN在内部把Tx输出回馈到Rx输入上,而完全忽略CANRX引脚的实际状态。发送的报文可以在CANTX引脚上检测到。 因此比较适合我们只有一块豆皮的情况下面测试 STM32 的 CAN 部分 BSP 程序。 ![]() ![]() |
3、STM32FVBT6 中的 can 物理引脚脚位可以设置成三种:默认模式,重定义地址1模式,重定义地址2模式。 在我们的豆皮中我们使用的是重定义地址2模式,即CANRX,CANTX 分别重定义到 PD0,PD1 引脚上面。 因此我们软件中第一步要进行重定义的操作: ![]() ![]() ------------------------------------------------------------------------ /* Configure CAN pin: RX */ //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //GPIO_Init(GPIOB, &GPIO_InitStructure); /* Configure CAN pin: TX */ //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //GPIO_Init(GPIOB, &GPIO_InitStructure); /* Configure CAN Remap 重影射 */ //GPIO_PinRemapConfig(GPIO_Remap1_CAN, ENABLE); ------------------------------------------------------------------------- /* Configure CAN pin: RX */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOD, &GPIO_InitStructure); /* Configure CAN pin: TX */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); /* Configure CAN Remap 重影射 */ GPIO_PinRemapConfig(GPIO_Remap2_CAN, ENABLE); ------------------------------------------------------------------------- /* Configure CAN pin: RX */ //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure CAN pin: TX */ //GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //GPIO_Init(GPIOA, &GPIO_InitStructure); ------------------------------------------------------------------------- 设置完 CAN 的引脚之后还需要打开 CAN 的时钟: /* CAN Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE); |
4、我们需要搞明白CAN波特率的设置,这个章节也是使用CAN的最重要的部分之一,因为这实际应用中我们需要根据我们实际的场合来选择 CAN 的波特率。 一般情况下面1M bps 的速率下可以最高可靠传输 40 米以内的距离。 在 50K 以下的波特率中一般可以可靠传输数公里远。 对于波特率的设置需要详细学习参考手册对应部分的解释。我们在调试软件的时候可以使用示波器来测试 CANTX 引脚上的波形的波特率,这样可以得到事半功倍的效果,大大的缩短调试学习的时间。 // *************************************************************** // BaudRate = 1/ NominalBitTime // NominalBitTime = 1tq+tBS1+tBS2 // tq = (BRP[9:0] + 1) x tPCLK // tPCLK = CAN's clock = APB1's clock // **************************************************************** 也就是BaudRate = APB1/((BS1 + BS2 + 1)*Prescaler) 这里注意的是采用点的位置,也就时BS1,BS2的设置问题,这里我也找了一些资料,抄录下来给大家,是CANopen协议中推荐的设置。 1Mbps速率下,采用点的位置在6tq位置处,BS1=5,BS2=2 500kbps速率下,采用点的位置在8tq位置处,BS1=7,BS2=3 250kbps速率下,采用点的位置在14tq位置处,BS1=13,BS2=2 125k,100k,50k,20k,10k的采用点位置与250K相同。 因此我们需要重视的有软件中的这么几个部分: //设置 AHB 时钟(HCLK) //RCC_SYSCLK_Div1 AHB 时钟 = 系统时钟 RCC_HCLKConfig(RCC_SYSCLK_Div8); //设置低速 AHB 时钟(PCLK1) //RCC_HCLK_Div2 APB1 时钟 = HCLK / 2 RCC_PCLK1Config(RCC_HCLK_Div2); // PLLCLK = 8MHz * 8 = 64 MHz //设置 PLL 时钟源及倍频系数 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_8); CAN 波特率设置中需要的就是PCLK1 的时钟。 CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack; CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; CAN_InitStructure.CAN_BS1=CAN_BS1_8tq; CAN_InitStructure.CAN_BS2=CAN_BS2_7tq; CAN_InitStructure.CAN_Prescaler=5; 通过上面部分的时钟设置我们已经可以算出我们的波特率了 CAN_bps = PCLK1 / ((1 + 7 + 8) * 5) = 25K bps 大家也可以实际测试中修改时钟值来通过示波器测试我们需要的波特率是否正确例如将PLLCLK 设置降低一半: // PLLCLK = 8MHz * 4 = 32 MHz //设置 PLL 时钟源及倍频系数 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_4); 那么我们得到的CAN_bps也会降低一半。 接下来还可以修改 HCLK 和 PCLK1 ,其实最终这几个分频和倍频值最终影响的都是 PCLK1。 ![]() ![]() ![]() ![]() 通过几次试验,相信大家应该很容易掌握波特率的设置了。 设置完波特率我们直接测试函数:TestStatus CAN_Polling(void) // CAN transmit at 25Kb/s and receive by polling in loopback mode TestRx = CAN_Polling(); if (TestRx == FAILED) { // Turn on led connected to PC.08 pin (LED4) // For DP-STM32F use LED4 connected to PC.12 GPIO_ResetBits(GPIOC, GPIO_Pin_12); } else { // Turn on led connected to PC.06 pin (LED2) // For DP-STM32F use LED2 connected to PC.11 GPIO_ResetBits(GPIOC, GPIO_Pin_11); } 大家可以仿真程序,当程序中 Test 等于 Passed 那么说明 Loopback 模式测试通过了。 ![]() ![]() 到此时说明如果大家只有一块CAN模块的时候学习可以告一个段落了,不过这个并不代表大家就已经掌握了 CAN 了,正真要掌握它,大家还是需要看大量的 CAN 部分的资料,参考手册部分的也是不够的,市面上有几本专门介绍现场总线和CAN总线的书,推荐大家买来经常翻翻看看,这样到需要实际应用的时候才可以做到如鱼得水。 |
5、完成了单板的 loopback 模式的测试之后接下来我们需要学习的就是多机通讯了,当然如果你只有一块豆皮开发板当然你就不能做这部分的试验了,只能先看看这部分的程序和教程了。 在这里我们需要准备两块豆皮板,使用三根线将 CANH,CANL,GND 三根线直连,当然需要把跳线 F 处的跳至终端电阻处,当两块板子都跳好后我们使用万用表测量下 CANH和CANL之间的电阻是否为 60 欧姆(豆皮上大约为 62欧姆)。 正常模式 在初始化完成后,软件应该让硬件进入正常模式,以便正常接收和发送报文。软件可以通过对CAN_MCR寄存器的INRQ位清’0’,来请求从初始化模式进入正常模式,然后要等待硬件对CAN_MSR寄存器的INAK位置’1’的确认。在跟CAN总线取得同步,即在CANRX引脚上监测到11个连续的隐性位(等效于总线空闲)后,bxCAN才能正常接收和发送报文。 不需要在初始化模式下进行过滤器初值的设置,但必须在它处在非激活状态下完成(相应的FACT位为0)。而过滤器的位宽和模式的设置,则必须在初始化模式中进入正常模式前完成。 准备工作做完我们需要设置软件,让一块豆皮板发送一块接收。 / CAN transmit at 100Kb/s and receive by interrupt in normal mode TestRx = CAN_Interrupt(); if (TestRx == FAILED) { // Turn on led connected to PC.09 pin (LED3) // For DP-STM32F use LED3 connected to PC.10 GPIO_ResetBits(GPIOC, GPIO_Pin_10); } else { // Turn on led connected to PC.07 pin (LED8) // For DP-STM32F use LED8 connected to PD.05 GPIO_ResetBits(GPIOD, GPIO_Pin_5); } |
豆皮 - STM32开发板入门教程(三) - SysTick 定时器 (原创) 版权所有 STMFANS 原创,转载请保留出处
http://www.stmfans.com/bbs/viewthread.php?tid=1069&page=1&extra=page%3D1 |
OK 下面继续做偶们的SysTick定时器咯 呵呵 本教程的主角是:SysTick |
通常实现Delay(N)函数的方法为: for(i = 0; i <= x; i ++); x --- 对应于 对应于N 毫秒的循环值 |
对于STM32系列微处理器来说,执行一条指令只有几十个ns,进行for循环时,要实现N毫秒的x值非常大,而且由于系统频率的宽广,很难计算出延时N毫 秒的精确值。针对STM32微处理器,需要重新设计一个新的方法去实现该功能,以实现在程序中使用Delay(N)。 |
Cortex-M3的内核中包含一个SysTick时钟。SysTick 为一个24位递减计数器,SysTick设定初值并使能后,每经过1个系统时钟周期,计数值就减1。计数到0时,SysTick计数器自动重装初值并继续计数,同时内部的COUNTFLAG标志会置位,触发中断(如果中断使能)。 在STM32的应用中,使用Cortex-M3 内核的SysTick作为定时时钟,设定每一毫秒产生一次中断,在中断处理函数 里对N减一,在Delay(N)函数中循环检测N是否为0,不为0则进行循环等待;若为0则关闭SysTick时钟,退出函数。 |
注: 全局变量TimingDelay 必须定义为volatile 延迟时间将不随系统时钟频率改变。 |
外部晶振为8MHz,9倍频,系统时钟为72MHz,SysTick的最高频率为9MHz(最大为HCLK/8),在这个条件下,把SysTick 效验值设置成9000,将SysTick 时钟设置为9MHz, 就能够产生1ms的时间基值,即SysTick产生1ms的中断。 |
使用ST的函数库使用systick的方法 1、调用SysTick_CounterCmd() 失能SysTick计数器 2、调用SysTick_ITConfig () 失能SysTick中断 3、调用SysTick_CLKSourceConfig() 设置SysTick时钟源。 4、调用SysTick_SetReload() 设置SysTick重装载值。 5、调用SysTick_ITConfig () 使能SysTick中断 6、调用SysTick_CounterCmd() 开启SysTick计数器 |
SysTick 配置函数 /******************************************************************************* * Function Name : SysTick_Config * Description : Configures SysTick * Input : None * Output : None * Return : None *******************************************************************************/ //SysTick设置 void SysTick_Config(void) { /* Disable SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Disable); /* Disable the SysTick Interrupt */ SysTick_ITConfig(DISABLE); /* Configure HCLK clock as SysTick clock source */ SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); /* SysTick interrupt each 1000 Hz with HCLK equal to 72MHz */ SysTick_SetReload(9000); /* Enable the SysTick Interrupt */ SysTick_ITConfig(ENABLE); } |
Delay_Ms 延迟一毫秒函数 /******************************************************************************* * Function Name : Delay_Ms * Description : Inserts a delay time. * Input : nTime: specifies the delay time length, in milliseconds. * Output : None * Return : None *******************************************************************************/ void Delay_Ms(u32 nTime) { /* Enable the SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Enable); TimingDelay = nTime; while(TimingDelay != 0); /* Disable SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Disable); /* Clear SysTick Counter */ SysTick_CounterCmd(SysTick_Counter_Clear); } |
TimingDelayMs_Decrement 中断调用函数 /******************************************************************************* * Function Name : TimingDelayMs_Decrement * Description : Decrements the TimingDelay variable. * Input : None * Output : TimingDelay * Return : None *******************************************************************************/ void TimingDelay_Decrement(void) { if (TimingDelay != 0x00) { TimingDelay--; } } |
SysTickHandler 中断进入函数 /******************************************************************************* * Function Name : SysTickHandler * Description : This function handles SysTick Handler. * Input : None * Output : None * Return : None *******************************************************************************/ void SysTickHandler(void) { TimingDelay_Decrement(); } |
NVIC_Configuration 中断向量表配置 /******************************************************************************* * Function Name : NVIC_Configuration * Description : Configures NVIC and Vector Table base location. * Input : None * Output : None * Return : None *******************************************************************************/ void NVIC_Configuration(void) { #ifdef VECT_TAB_RAM /* Set the Vector Table base location at 0x20000000 */ NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif } |
完整工程在附件里 敬请继续关注 豆皮的的教程会逐步推出 |
豆皮 - STM32开发板入门教程(二) - 按键 + 蜂鸣器 (原创) 版权所有 STMFANS 原创,转载请保留出处
http://www.stmfans.com/bbs/viewthread.php?tid=1064&extra=page%3D1 |
呵呵 在教程一的基础上 继续做偶们的按键和蜂鸣器 呵呵 豆皮板子上一共有5个按键(4个功能按键 + 一个RESET按键) 那么 选择 LED5 LED6 LED7 LED8 *博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。 ![]() 最近文章
|