给自己的学习总结帖~~ 这里仅都是c语言 嵌入式相关代码第7季啊
一、STM32通过PWM控制电机速度
做STM32智能小车的实验中会用到定时器PWM输出,来改变直流电机的转速。分享本文了解如何通过PWM实现对电机速度的控制。
PWM控制电机速度的基本原理
PWM(Pulse Width Modulation),也就是脉冲宽度调制。
PWM中有一个比较重要的概念,占空比:是一个脉冲周期内有效电平在整个周期所占的比例。
为了实现IO口上电压的持续性变化,可以调节PWM的占空比。这也能够使外设的功率进行持续性变化,最终控制直流电机转速的快慢。如何调节PWM波形的输出就是重点。
上图中的ARR是我们给定时器的一个预装载值,CCRx的上下变化是产生PWM波的关键。我们假设ARR大于CCRx的部分输出为高电平(即t1-t2、t3-t4、t5-t6),ARR小于CCRx的部分输出为低电平(即0-t1、t2-t3、t4-t5),则改变CCRx的值就能改变输出PWM的占空比。因此,想要控制PWM的输出波形,重要的就是如何设置ARR与CCRx这两个寄存器的值了。
STM32定时器中断
为了便于理解接下来关于PWM应用的内容,先插一段定时器中断的知识。
产生定时中断是定时器的用法之一,与定时器用来进行PWM输出和输入捕获相比,定时器中断更容易理解、掌握。
原理简介
使用通用定时器进行中断的原理,其实和开发板Systick定时器进行中断延时很相似(Stm32入门——Systick定时器),即:用psc(预分频系数)设置好定时器时钟后,arr(预装载值)在每个时钟周期内减1,当arr减为0时触发中断然后进入中断处理程序进行中断处理。以下代码为例:
RCC->APB1ENR|=1<<1
解释一下上面这行代码,由于定时器3(TIM3)是挂在APB1上的外设,所以要打开APB1,这里的预分频器值psc是来设置TIM3的时钟频率的,如果系统时钟(SYSTICK)频率为72MHz、psc为7199,则TIM3的时钟频率就为:
72MHz/(7199+1)Hz = 10KHz //这里的“+1”是手册中规定的。
10KHz是什 么意思呢?就是一秒钟会产生10K个周期,那么一个周期的时间长度就是1/10KHz,如果你想将定时器中断的时间间隔设置为0.5秒,那么你将arr设置为5000即可,因为arr每减1就需要一个周期的时间,减5000次就经过了5000*(1/10KHz)=0.5秒。
TIM3->DIER|=1<<0
再解释下上面这一行,设置允许更新中断,即arr减到0以后可以触发更新中断,还有其他类型的中断。
MY_NVIC_Init(1,3,TIM3_IRQn,2);//抢占1,子优先级3,组2
看上面这行代码,中断优先级有抢占优先级和响应(即子优先级)优先级两种,抢占优先级即:若程序1正在使用CPU,这时如果程序2要求使用CPU,并且程序2的抢占优先级高,则CPU被程序2抢占;若两者抢占优先级相同,则就算程序2的响应优先级高于程序1,CPU也不能被抢占;若程序1正在使用CPU,程序2和程序3的抢占优先级等于或低于程序1,且程序2的响应优先级高于程序三,则待CPU空出后,程序2先运行,程序3最后运行。TIM3_IRQn是指定将要运行的中断处理程序号。“组2”是设置中断优先级分组的,这是因为寄存器提供了四位来设置优先级,组2代表的是前两位给抢占优先级,后两位给响应优先级。
PWM模式、有效电平
前面介绍完中断,再说一下PWM工作原理。
假设上图中ARR大于CCRx时输出为高电平,ARR小于CCRx时输出为低电平,但在实际运用中可能并非如此,有可能是相反的情况——ARR大于CCRx时输出为低电平,ARR小于CCRx时输出为高电平,至于到底是哪种情况,还要看PWM是哪种模式、有效电平又设置的是何种极性了。
- 模式1:ARR小于CCRx时输出为“有效”电平,ARR大于CCRx时输出为“无效”电平。
- 模式2:ARR小于CCRx时输出为“无效”电平,ARR大于CCRx时输出为“有效”电平。
这里说的是“有效”和“无效”,而不是“高”和“低”,也就是说有效电平可高可低,并非一定就是高电平。PWM模式、效电平极性,需要程序员自己配置相关的寄存器来实现。通过下面的代码来讲解。
TIM1_PWM_Init(899,0);//不分频。PWM频率=72000/(899+1)=80Khz
上一小节讲过关于定时器参数的设置。使用定时器1的通道1来输出一路PWM波,这里的899设置的就是ARR的值,至于那个0是用来设置TIM1的频率的,不分频就代表TIM1的时钟频率和系统时钟相同,这里假设为72MHz。
void TIM1_PWM_Init(u16 arr,u16 psc)
{
//此部分需手动修改IO口设置
RCC->APB2ENR|=1<<11; //TIM1时钟使能
GPIOA->CRH&=0XFFFFFFF0; //PA8清除之前的设置
GPIOA->CRH|=0X0000000B; //复用功能输出
TIM1->ARR=arr; //设定计数器自动重装值
TIM1->PSC=psc; //预分频器设置
TIM1->CCMR1|=7<<4; //CH1 PWM2模式
TIM1->CCMR1|=1<<3; //CH1预装载使能
TIM1->CCER|=0<<1; //OC1 输出使能
//TIM1->CCER|=1<<1;
TIM1->BDTR|=1<<15; //MOE 主输出使能
TIM1->CR1=0x0080; //ARPE使能
TIM1->CR1|=0x01; //使能定时器1
}
下文具体分析上面的代码。
前面4-6行是用来配置GPIO口的。
TIM1->ARR=arr; //设定计数器自动重装值
TIM1->PSC=psc; //预分频器设置
这两行就是我上门提到的设置定时器的频率和重装载值。
TIM1->CCMR1|=7<<4; //CH1 PWM2模式
TIM1->CCMR1|=1<<3; //CH1预装载使能
TIM1->CCER|=0<<1; //OC1 输出使能
这三行是用来设置PWM输出模式和设置通道的,通道是什么呢?简单地讲就是输出PWM波的GPIO口,代码一开始不是设置了PA8这个GPIO口嘛,这个PA8就是通道1。使用通道的话要先进行输入输出方向、通道使能的设置。
TIM1->CCER|=1<<1;
这行代码是用来设置“有效电平”极性的,根据手册,当TIM1->CCER[1]这位置1时,有效电平为低电平,置0时有效电平为高电平,而默认情况下置0。
TIM1->BDTR|=1<<15; //MOE 主输出使能
这行代码只要对高级定时器进行设置,普通定时器无需设置。
TIM1->CR1=0x0080; //ARPE使能
这行代码是用来使能ARPE,ARPE是什么呢,就是当它被置1时,你自己设置的CCRx会立即生效,如果它被置为0,那么你自己设置的CCRx值不会立即生效(可能之前ARPE已经有值了),而是当之前设置的CCRx生效后才会使用你最新设置的CCRx值。
上面的代码里没有对CCRx进行设置,这是因为CCRx常常是一个变化的值,你可以在主函数中用一个for循环+if判断语句对它进行++或–的操作,从而达到连续改变CCRx值得目的,例如:
PWM波的周期是由定时器时钟频率和预装载值两者决定的,预装载值就是ARR。
预装载值PSC设置为899,那么,当定时器的当前值val从0增加到899时,一共经过了900个时钟周期,这900个时钟周期会产生一个PWM波形,也就是说900个定时器时钟周期才相当于一个PWM周期,那么PWM的频率就为72MHz/900=80KHz,周期为1/80KHz。
二、STM32的半主机机制
半主机机制的作用
半主机是用于ARM目标的一种机制,可将来自STM32单片机应用程序的输入输出请求传送至运行仿真器的PC主机。使用此机制可以启用C库中的函数,如printf()和scanf(),来使用PC主机的屏幕和键盘。这样就可以看到单片机的输入输出,方便进行调试。注意:这种机制的运行需要仿真器,否则无法运行。
简单的来说,半主机模式就是通过仿真器实现开发板在电脑上的输入和输出。
开发时单片机需要独立运行,开发者就应该去掉仿真器,把printf函数通过单片机的外设来实现,例如通过开发板的串口。
非半主机机制下printf函数的实现方法
Use MicroLIB(微库)
因为使用微库的话,不会使用半主机模式。
如下图,在点开MDK软件的魔术棒,勾选Target选项卡中的“Use MicroLIB”。这样就可以使用printf()函数,通过USART输出数据到电脑串口助手。
在主程序中添加代码
不使用Use MicroLIB(微库),就要在工程中加入以下代码, 以支持printf函数 。代码是写在USART的初始化文件中,记得修改USARTx,换成你要输出的USART端口号。
/******************************************************************************
* 【功 能】 printf函数重定向支持代码
* 加入以下代码, 使用printf函数时, 不再需要选择use MicroLIB
* 参 数:
* 返回值:
******************************************************************************/
#pragma import(__use_no_semihosting) //为确保没有从C库链接使用半主机的函数
struct __FILE { int handle; }; // 标准库需要的支持函数
FILE __stdout; // FILE 在stdio.h文件
void _sys_exit(int x) // 定义_sys_exit()以避免使用半主机模式
{ x = x; }
int fputc(int ch, FILE *f) // 重定向fputc函数,使printf的输出,由fputc输出到UART, 这里使用串口1(USART1)
{
//if(xFlag.PrintfOK == 0) return 0; // 判断USART是否已配置,防止在配置前调用printf被卡死
while((USARTx ->SR&0X40)==0); // 等待上一次串口数据发送完成
USARTx ->DR = (u8) ch; // 写DR,串口1将发送数据
return ch;
}
三、单片机通过USB(U盘)升级固件的方法
单片机升级固件的方法有很多中, 比如:ISP(在系统编程)、ICP(在电路编程)、IAP(在应用编程)等 ( 看下面四
今天结合瑞萨单片机(RA4M3)、e2 studio开发环境给大家讲述的是单片机通过USB(U盘)升级固件的方法。
RA4M3通过USB实现固件升级
01
固件更新应用案例
(1)修复程序错误
(2)功能升级
02
首先必须规划好Bootloader和Application的空间分配。如下图所示,案例使用R7FA4M3AF3CFB,有1M的ROM。Bootloader从0x000000开始存储,结束地址为0x1FFFF,所占空间大小为0x20000;Application从0x20000开始存储,结束地址为0x000FFFFF,所占空间大小为0xE0000。
03
创建一个Bootloader工程,添加USB_HMSC、Flash、RTC和CRC等外设。其中USB添加主机大容量存储类(HMSC)驱动程序,它实现了USB HMSC接口,可从USB大容量存储设备中读取、写入和擦除数据。
04
使用RTC设置系统时间。
05
初始化flash驱动,然后在data flash中读取设备设置的log。
06
初始化USB驱动,等待USB设备插入,如果检测到有USB设备插入,将会根据文件"0:Put Image Here/update.txt"中的内容,找到相应的***.srec文件,并打开文件读写数据(将文件内容解析后烧写到Application对应的地址空间),读写完成后将文件名改为"0:Put Image Here/loaded.txt",表示已完成数据读写。
07
根据信息类型擦除data flash中的数据,擦除成功后写入新的设备设置参数。
08
在属性→设置中输出srec格式文件作为Bootloader烧录文件。
09
将Bootloader烧录到EK-RA4M3开发板上。首先,利用J-Flash Lite将RA4M3整片擦除,然后将ra4m3_hmsc_noRTOS.srec烧写到芯片中,由于此时app部分内容为空,红色LED闪烁(按下reset大概10秒后,红色LED闪烁)。
10
打开e2 studio新建Application工程,用来验证可以通过Bootloader成功升级Application,若成功升级,三颗LED灯会不停地闪烁。
11
打开memory_regions.ld文件,FLASH_LENGTH = 0x100000;更新为FLASH_LENGTH = 0xE0000;
由于Bootloader占用了0x20000大小,因此对于EK-RA4M3来说,仅剩下0x100000-0x20000=0xE0000大小可供Application使用。
12
Application工程生成srec格式文件,下图红框位置勾选 Section: -j .text & -j .data,使得application project仅生成code flash对应的内容。
13
准备一个U盘(FAT32格式),在里面建一个文件夹,命名为“Put Image Here”,将RA4M3_BLINKY.srec文件添加进去
14
update.txt文件里面添加RA4M3_BLINKY.srec
15
将U盘插入,开发板上电,当前运行的代码是Bootloader,由于此时尚未执行Application升级,因此LED2常亮,按下复位键,使得Bootloader重新运行,执行Application代码升级,可以看到LED灯按照RA4M3_BLINKY程序闪烁。将U盘插回电脑查看“Put Image Here”文件夹,可以看到update.txt变为loaded.txt,程序升级成功。
四、单片机三种烧录方式ISP、IAP和ICP有什么不同?
学习单片机的同学,或多或少都听说过ISP、IAP和ICP,都是对单片机进行编程,下面简单来说下这几个内容以及区别。
ISP
ISP:In System Programing,在系统编程。
ISP 是指可以在板级上进行编程,也就是不用拆芯片下来,写的是整个程序,一般是通过 ISP 接口线来写。
支持ISP的芯片一般在芯片内部固化了一段(用ISP升级的)boot程序。
比如:使用STC-ISP对STC芯片编程,利用Flash loader对STM32编程等。
ICP
ICP:In Circuit Programing,在电路编程。
ICSP:In-Circuit Serial Programming,在电路串行编程。如:对EEPROM编程等。
ICP编程方式网上各有说法,从字面含义(在电路)来说,所有处于编程的芯片都需要上电,都处于电路中。不严格来说利用 J-Link、ST-Link、 e-Link32等工具进行编程也属于在电路编程(ICP)。
在维基百科中,ISP(在系统编程),也称为在电路串行编程(ICSP)。
IAP
IAP:In applicaTIng Programing,在应用编程。在程序运行的过程中进行编程(升级程序,更新固件)。
IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
[来自:百度百科]
IAP通信口
IAP的通信口有许多种:UART串口、ETH以太网、I2C、SPI......等。按理说只要能传输数据的通信口都能实现IAP的功能。
IAP的应该非常广泛
ISP 与 IAP 的区别
1.打个比喻吧
1、ISP 是把房子拆了再重造一间,那么在造好之前当然是不能住人的啦!
2、IAP 是在造好的房子里边进行一些装修,当然人可以继续住啦!
2.应用场合
1,ISP 程序升级需要到现场解决,不过好一点的是不必拆机器了;
2,IAP 如果有网管系统的话,用网管下载一切搞定,人不用跑来跑去,
3.ISP 和 IAP 的工作原理
ISP 的实现相对要简单一些,一般通用做法是内部的存储器可以由上位机的软件通过串口来进行改写。对于单片机来讲可以通过 SPI 或其它的串行接口接收上位机传来的数据并写入存储器中。所以即使我们将芯片焊接在电路板上,只要留出和上位机接口的这个串口,就可以实现芯片内部存储器的改写,而无须再取下芯片。
IAP 的实现相对要复杂一些,在实现 IAP 功能时,单片机内部一定要有两块存储区,一般一块被称为 BOOT 区,另外一块被称为存储区。单片机上电运行在 BOOT 区,如果有外部改写程序的条件满足,则对存储区的程序进行改写操作。如果外部改写程序的条件不满足,程序指针跳到存储区,开始执行放在存储区的程序,这样便实现了 IAP 功能。
4.ISP 和 IAP 的优点
ISP 技术的优势是不需要编程器就可以进行单片机的实验和开发,单片机芯片可以直接焊接到电路板上,调试结束即成成品,免去了调试时由于频繁地插入取出芯片对芯片和电路板带来的不便。
IAP 技术是从结构上将 Flash 存储器映射为两个存储体,当运行一个存储体上的用户程序时,可对另一个存储体重新编程,之后将程序从一个存储体转向另一个。
ISP 的实现一般需要很少的外部电路辅助实现,而 IAP 的实现更加灵活,通常可利用单片机的串行口接到计算机的 RS232 口,通过专门设计的固件程序来编程内部存储器,可以通过现有的 INTERNET 或其它通讯方式很方便地实现远程升级和维护。