1. 了解Cortex-M3的GPIO使用及其相关的API函数;
2. 掌握Cortex-M3读取GPIO引脚状态的方法。
3. 掌握Cortex-M3的GPIO引脚输出控制的方法。
1. 实验原理
通用输入/输出端口GPIO模块可由多达9个物理GPIO模块组成,每个对应一个独立的GPIO端口。GPIO支持多达65个可编程的输入/输出引脚,具体数量取决于正在使用的器件型号。GPIO具有以下特性:
① 可编程控制GPIO中断,包括屏蔽中断发生、边沿触发(上升沿,下降沿,上升、下降沿)和高/低电平触发;
② 输入/输出可承受5V电压;
③ 在读和写操作中通过地址线进行位屏蔽;
④ 可编程控制GPIO引脚(pad)配置,包括弱上拉或下拉电阻、2mA/4mA/8mA引脚驱动、8mA驱动的斜率控制、开漏使能和数字输入使能。
数据控制寄存器允许用软件来配置GPIO的操作模式。数据方向寄存器将GPIO配置为输入或输出,而数据寄存器则捕获输入的数据或驱动数据从引脚输出。
GPIO方向GPIODIR(GPIO Direction)寄存器用来将每个独立的引脚配置为输入或输出。当数据方向设为0时,GPIO配置为输入,并且对应的数据寄存器位将捕获和储存GPIO端口上的值。当数据方向位设为1时,GPIO配置为输出,并且对应的数据寄存器位将在GPIO端口上输出。
为了提高软件的效率,通过将地址总线的位[9:2]用做屏蔽位,GPIO端口允许对GPIO数据GPIODATA(GPIO data)寄存器中的各个位进行修改。这样软件驱动程序仅使用一条指令就可以对各个GPIO引脚进行修改,而不会影响其它引脚的状态。为了提供这种特性寄存器GPIODATA覆盖了存储器映射中的256个位置。
默认情况下,除了用做JTAG/SWD功能的GPIO信号外,所有其他的GPIO信号都配置为非驱动(三态)。它们的数字功能被禁止,不驱动引脚上的逻辑值且不允许引脚电压进入GPIO接收器。为了使用引脚的数字功能(GPIO或复用功能),相应的GPIODEN位必须置位。
1)GPIO开关量输入
将GPIO连接数字开关量对应的管脚设置在输入状态;然后,写一个循环,不停地去检测输入引脚的状态。
2)GPIO输出驱动继电器(或光电隔离器)
将相关的GPIO设置成输出状态,并且要注意配置输出的驱动电流大小;然后,写一个循环,依次输出变化的高低电平,从而控制继电器及光电隔离器的关断与打开。
2. 创建新工程的步骤
1)创建一个目录,例如D:\DEMO
2)创建工作区,选择主菜单的File>New>Workspace命令,然后开启一个空白工作区窗口
3)生成新项目
(1) 选择主菜单Project>Create New Project,弹出生成新项目窗口。选择Empty project 。
(2) 在Tool chain栏中选择ARM,点击OK按钮,弹出另存为窗口。
(3) 在“另存为”窗口中浏览和选择新建的D:\DEMO目录,输入新项目的文件名为demo,然后保存,这时在屏幕左边的Workspace窗口中将显示新建的项目名和输出代码模式。
4)保存工作区
5)添加/新建文件
(1)建立文件组,右击“demo->Debug”然后选择ADD>ADD Group,新建三个文件组:startup文件组,src文件组 和lib文件组。
(2) 向文件组添加对应文件(对应文件在相应目录中查找)
① 在lib组中添加driverlib.r79文件;
② 在startup组中添加startup.c文件;
③ 在src组中添加main.c文件或其他新建的 *.c文件。
3. 实验内容:
1. 编程实现读取GPIO引脚上对应的开关量状态,改变开关量,观察能否成功读回;
2. 编程实现Cortex-M3的GPIO引脚驱动继电器及光电隔离器,观察继电器的动作以及发光管的亮灭变化;
1) 硬件电路如下(程序后面)
2)程序如下
(1)系统初始化头文件systemInit.h
#ifndef SYSTEM_INIT_H
#define SYSTEM_INIT_H
/*包含必要的头文件*/
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
/*声明全局的系统时钟变量*/
extern unsigned long TheSysClock;
/*系统时钟初始化*/
extern void clockInit(void);
#endif // __SYSTEM_INIT_H__
(2)系统初始化实现文件systemInit.c
#include "systemInit.h"
/*定义全局的系统时钟变量*/
unsigned long TheSysClock = 12000000UL;
/*定义KEY*/
#define KEY_PERIPH SYSCTL_PERIPH_GPIOB
#define KEY_PORT GPIO_PORTB_BASE
#define KEY_PIN GPIO_PIN_5
/*系统时钟初始化*/
void clockInit(void) {
SysCtlLDOSet(SYSCTL_LDO_2_50V); //设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | //系统时钟设置
SYSCTL_OSC_MAIN | //采用主振荡器
SYSCTL_XTAL_6MHZ | //外接6MHz晶振
SYSCTL_SYSDIV_1); //不分频
TheSysClock = SysCtlClockGet(); //获取当前的系统时钟频率
}
(3)主程序main.c
#include "hw_types.h"
#include "hw_memmap.h"
#include "hw_gpio.h"
#include "systemInit.h"
#include "sysctl.h"
#define DATA_PERIPH SYSCTL_PERIPH_GPIOB //定义PB口
#define DATA_PORT_BASE GPIO_PORTB_BASE
#define DATA_PORT_PIN (GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|
GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7)
#define DATA_PORT_BASE_PIN DATA_PORT_BASE,DATA_PORT_PIN
#define DATA_PORT GPIOPinRead(DATA_PORT_BASE_PIN)
#define OUT_PERIPH SYSCTL_PERIPH_GPIOD //定义PD口
#define OUT_PORT_BASE GPIO_PORTD_BASE
#define OUT_PORT_PIN (GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|
GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5)
#define OUT_PORT_BASE_PIN OUT_PORT_BASE,OUT_PORT_PIN
#define OUT2_PERIPH SYSCTL_PERIPH_GPIOC //定义PC口
#define OUT2_PORT_BASE GPIO_PORTC_BASE
#define OUT2_PORT_PIN GPIO_PIN_6|GPIO_PIN_7
#define OUT2_PORT_BASE_PIN OUT2_PORT_BASE,OUT2_PORT_PIN
void delay(unsigned int num) {
unsigned int a;
a=num;
for(;a>0;a--);
}
int main(void){
static char value;
clockInit();
SysCtlPeripheralEnable(DATA_PERIPH); //使能相应的GPIO
GPIOPinTypeGPIOInput(DATA_PORT_BASE_PIN);//设置相应的数据管脚为输入
SysCtlPeripheralEnable(OUT_PERIPH); //使能相应的GPIO
GPIOPinTypeGPIOOutput(OUT_PORT_BASE_PIN); //设置相应的数据管脚为输入
GPIOPadConfigSet(OUT_PORT_BASE,OUT_PORT_PIN,GPIO_STRENGTH_8MA,
GPIO_PIN_TYPE_STD_WPU);
SysCtlPeripheralEnable(OUT2_PERIPH); //使能相应的GPIO
GPIOPinTypeGPIOOutput(OUT2_PORT_BASE_PIN);//设置相应的数据管脚为输入
GPIOPadConfigSet(OUT2_PORT_BASE,OUT2_PORT_PIN,GPIO_STRENGTH_8MA,
GPIO_PIN_TYPE_STD_WPU);
while(1) //无限循环
value= DATA_PORT; //读数据口数值
delay(1000000); //置断点,观察value值的变换
GPIOPinWrite(OUT_PORT_BASE,OUT_PORT_PIN,value);
GPIOPinWrite(OUT2_PORT_BASE,OUT2_PORT_PIN,value);
delay(1000000);
}
}
(4)实验结果
当把可执行文件加载到硬件电路后,运行-按下按键,一段时间后继电器吸合,当断开按键后,一段时间后继电器打开。
3)实验中存在的问题及处理方法
(1)IAR软件安装问题
刚接触IAR这个软件,在软件安装的过程中就出现了问题,在拷贝系统头文件(.h文件)和源文件(.c文件)的时候,由于文件较多,出现了漏拷文件的现象,所以在运行程序的时候总会报一些头文件找不到或打不开的错误。
解决办法:认真细心地重复拷一遍
(2)运行过程环境变量配置的问题
由于环境变量配置比较多,所以在这个环节往往会出现环境变量漏配置的问题或配置错误,这会使程序无法运行
解决办法:认真细心地一步一步地配置
4. 收获和体会
5. 思考题
1) GPIO的推挽输出和开漏输出在应用上有何区别?
推挽输出:可以输出高、低电平,连接数字器件
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。
开漏电路:就是指以MOSFET的漏极为输出的电路。一般的用法是会在漏极外部的电路添加上拉电阻。完整的开漏电路应该由开漏器件和开漏上来电阻组成。
2) 如何解决开漏模式下上拉电压不足的问题?
提高上拉电压或减小上拉电阻。
3)施密特触发输入有何作用?
实验2 定时器实验
1. 了解Cortex-M3的定时/计数器使用及其相关的API函数;
2. 掌握Cortex-M3的定时/计数器使用方法与关键步骤;
3. 了解Cortex-M3的看门狗定时器使用及其相关的API函数;
4. 掌握Cortex-M3的看门狗定时器使用方法与关键步骤。
1. 实验原理
定时器是对固定频率脉冲进行计数,用计数值计算时间;而定时器是对外部输入脉冲计数。可编程定时器可驱动定时器输入引脚的外部事件进行计数或定时。Stellaris通用定时器模块GPTM(General-Purpose Timer Module)包含4个GPTM模块(定时器0、定时器1、定时器2和定时器3)。每个GPTM模块包含两个16位的定时器/计数器(称为TimerA和TimerB),用户可以将它们配置成独立运行的定时器或事件计数器,或将它们配置成1 个32 位定时器或一个32位实时时钟RTC(Real-Timer Clock)。定时器可用于触发模/数(A/D)转换等。可编程定时器支持一下模式:
☆ 32位定时器模式 单次触发定时器、周期(periodic)定时器和实时时钟;
☆ 16位定时器模式 单次触发(one-shot)定时器和周期定时器;
☆ 16位输入捕获模式 输入边沿计数捕获和输入边沿定时捕获;
☆ 16位PWM(Pulse-Width Modulation)模式。
1)计数器实验
首先,将相应的定时/计数器配置好(使能,计数初值的装载等);然后,注册相应的中断,编写中断服务程序;最后,使能中断,进入一个死循环中,当计数满5次后,计数器溢出,触发标志位,产生中断,进入到中断服务程序中执行。
2)秒时钟发生器实验
首先,将相应的定时/计数器配置好(使能,定时初值的装载等);然后,注册相应的中断,编写中断服务程序;最后,使能中断,进入一个死循环中,当定时时间到后,产生中断,进入到中断服务程序中执行。
3)看门狗定时器实验
首先,使能看门狗定时器的定时功能,装载初值;然后,注册相应的中断,编写中断服务程序;最后,使能中断,进入一个死循环中,当时间到后,产生中断,进入到中断服务程序中执行,即翻转一个GPIO引脚电平,产生方波输出。
3. 实验内容
1)计数器实验
编程实现利用Cortex-M3的定时/计数器引脚对外部脉冲进行计数,每计数满5个后产生计数中断。
(1) 硬件电路如下
(2) 程序如下:
① 头文件systemInit.h
#ifndef __SYSTEM_INIT_H__
#define __SYSTEM_INIT_H__
/*包含必要的头文件*/
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
/*声明全局的系统时钟变量*/
extern unsigned long TheSysClock;
/*系统时钟初始化*/
extern void clockInit(void);
#endif // __SYSTEM_INIT_H__
② 实现文件systemInit.c
#include "systemInit.h"
/*定义全局的系统时钟变量*/
unsigned long TheSysClock = 12000000UL;
/*定义KEY*/
#define KEY_PERIPH SYSCTL_PERIPH_GPIOB
#define KEY_PORT GPIO_PORTB_BASE
#define KEY_PIN GPIO_PIN_5
/*系统时钟初始化*/
void clockInit(void) {
SysCtlLDOSet(SYSCTL_LDO_2_50V); //设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | //系统时钟设置
SYSCTL_OSC_MAIN | //采用主振荡器
SYSCTL_XTAL_6MHZ | //外接6MHz晶振
SYSCTL_SYSDIV_1); //不分频
TheSysClock = SysCtlClockGet(); //获取当前的系统时钟频率
}
③ 主程序main.c
#include "hw_types.h"
#include "hw_memmap.h"
#include "hw_gpio.h"
#include "systemInit.h"
#include "sysctl.h"
#include "hw_ints.h"
#include "gpio.h"
#include "systick.h"
#include "timer.h"
#include "interrupt.h"
#define KEY_PERIPH SYSCTL_PERIPH_GPIOD //定义PD口
#define KEY_PORT_BASE GPIO_PORTD_BASE
#define KEY GPIO_PIN_4
#define LED_PERIPH SYSCTL_PERIPH_GPIOD //定义PC口
#define LED_PORT_BASE GPIO_PORTD_BASE
#define LED GPIO_PIN_0
#define LED_PORT_BASE_PIN LED_PORT_BASE,LED
/*定时器16位输入边沿计数捕获功能初始化*/
void timerInitCapCount(void) {
SysCtlPeripheralEnable(KEY_PERIPH);
GPIOPinTypeTimer(KEY_PORT_BASE, KEY);//配置CCP0管脚为脉冲输入
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);//使能Timer模块
TimerConfigure(TIMER0_BASE,
TIMER_CFG_16_BIT_PAIR | //配置Timer为16位事件计数器
TIMER_CFG_A_CAP_COUNT);
TimerControlEvent(TIMER0_BASE, //控制TimerA捕获CCP负边沿
TIMER_A,TIMER_EVENT_NEG_EDGE);
TimerLoadSet(TIMER0_BASE, TIMER_A, 10); //设置计数器初值
TimerMatchSet(TIMER0_BASE, TIMER_A, 5); //设置事件计数匹配值
TimerIntEnable(TIMER0_BASE, TIMER_CAPA_MATCH);//使能TimerA捕获匹配中断
IntEnable(INT_TIMER0A);//能Timer中断
IntMasterEnable( );//使能处理器中断
TimerEnable(TIMER0_BASE, TIMER_A);//使能Timer计数
}
void Timer0A_ISR(void) { //计数器中断
TimerIntClear(TIMER0_BASE, TIMER_CAPA_MATCH);//清除定时器0中断。
TimerLoadSet(TIMER0_BASE, TIMER_A, 10);//重装定时器装载值为10
GPIOPinWrite(LED_PORT_BASE,LED, GPIOPinRead(LED_PORT_BASE,LED)^LED);//翻转LED端口。
TimerEnable(TIMER0_BASE, TIMER_A);//使能定时器0。
}
int main(void) {
clockInit();
timerInitCapCount();
Timer0A_ISR();
SysCtlPeripheralEnable(LED_PERIPH);//使能相应的GPIO
GPIOPinTypeGPIOOutput(LED_PORT_BASE_PIN);//设置相应的数据管脚为输出
GPIOPadConfigSet(LED_PORT_BASE,LED,GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD_WPU);
while(1) ; //无限循环
}
2)秒时钟发生器实验
编程实现利用Cortex-M3的定时/计数器的精确定时功能,产生一个频率为1Hz的时钟输出。
(1) 硬件电路如下:
(2) 程序如下:
① 系统初始化头文件systemInit.h
#ifndef __SYSTEM_INIT_H__
#define __SYSTEM_INIT_H__
/*包含必要的头文件*/
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
/*声明全局的系统时钟变量*/
extern unsigned long TheSysClock;
/*系统时钟初始化*/
extern void clockInit(void);
#endif // __SYSTEM_INIT_H__
② 系统初始化实现文件systemInit.c
#include "systemInit.h"
/*定义全局的系统时钟变量*/
unsigned long TheSysClock = 12000000UL;
/*定义KEY*/
#define KEY_PERIPH SYSCTL_PERIPH_GPIOB
#define KEY_PORT GPIO_PORTB_BASE
#define KEY_PIN GPIO_PIN_5
/*系统时钟初始化*/
void clockInit(void) {
SysCtlLDOSet(SYSCTL_LDO_2_50V); //设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | //系统时钟设置
SYSCTL_OSC_MAIN | // 采用主振荡器
SYSCTL_XTAL_6MHZ | //外接6MHz晶振
SYSCTL_SYSDIV_1); // 不分频
TheSysClock = SysCtlClockGet(); // 获取当前的系统时钟频率
}
③ 主程序main.c
#include "hw_types.h"
#include "hw_memmap.h"
#include "hw_gpio.h"
#include "systemInit.h"
#include "sysctl.h"
#include "hw_ints.h"
#include "gpio.h"
#include "systick.h"
#include "timer.h"
#include "interrupt.h"
#define LED_PERIPH SYSCTL_PERIPH_GPIOD //定义PC口
#define LED_PORT_BASE GPIO_PORTD_BASE
#define LED GPIO_PIN_0
#define LED_PORT_BASE_PIN LED_PORT_BASE_LED
void Timer0A_ISR(void) { //计数器中断
TimerIntClear( TIMER0_BASE, TIMER_CAPA_MATCH );//清除定时器0中断
GPIOPinWrite(LED_PORT_BASE,LED,GPIOPinRead(LED_PORT_BASE,LED)^LED);//翻转LED 端
TimerEnable(TIMER0_BASE, TIMER_A); //使能定时器0
}
int main(void) {
clockInit();
SysCtlPeripheralEnable( SYSCTL_PERIPH_TIMER0 );
TimerConfigure(TIMER0_BASE,TIMER_CFG_32_BIT_PER);//设置定时器0为周期触发模TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet());//设置定时器装载值:定时1秒TimerIntEnable(TIMER0_BASE,TIMER_TIMA_TIMEOUT);//设置定时器为溢出中断
TimerEnable(TIMER0_BASE, TIMER_A);//使能定时器0
IntEnable(INT_TIMER0A);//使能定时器0外设
IntMasterEnable(); SysCtlPeripheralEnable(LED_PERIPH);//使能相应的GPIO GPIOPinTypeGPIOOutput(LED_PORT_BASE_PIN);//设置相应的数据管脚为输出 GPIOPadConfigSet(LED_PORT_BASE,LED,GPIO_STRENGTH_8MA,
GPIO_PIN_TYPE_STD_WPU );
while(1); //无限循环
}
3)看门狗定时器实验
编程实现利用Cortex-M3的看门狗定时器功能,产生一个方波输出。
(1) 硬件电路同2)
(2) 程序如下:
① 系统初始化头文件systemInit.h
#ifndef __SYSTEM_INIT_H__
#define __SYSTEM_INIT_H__
/*包含必要的头文件*/
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
/*声明全局的系统时钟变量*/
extern unsigned long TheSysClock;
/*系统时钟初始化*/
extern void clockInit(void);
#endif // __SYSTEM_INIT_H__
② 系统初始化实现文件systemInit.c
#include "systemInit.h"
/*定义全局的系统时钟变量*/
unsigned long TheSysClock = 12000000UL;
/*定义KEY*/
#define KEY_PERIPH SYSCTL_PERIPH_GPIOB
#define KEY_PORT GPIO_PORTB_BASE
#define KEY_PIN GPIO_PIN_5
/*系统时钟初始化*/
void clockInit(void) {
SysCtlLDOSet(SYSCTL_LDO_2_50V); //设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | //系统时钟设置
SYSCTL_OSC_MAIN | //采用主振荡器
SYSCTL_XTAL_6MHZ | //外接6MHz晶振
SYSCTL_SYSDIV_1); //不分频
TheSysClock = SysCtlClockGet(); // 获取当前的系统时钟频率
}
③ 主程序mian.c
#include "systemInit.h"
#include "watchdog.h"
/*定义LED*/
#define LED_PERIPH SYSCTL_PERIPH_GPIOD
#define LED_PORT GPIO_PORTD_BASE
#define LED_PIN GPIO_PIN_0
#define LED_PIN1 GPIO_PIN_1
/*LED初始化*/
void ledInit(void) {
SysCtlPeripheralEnable(LED_PERIPH);//使能LED所在的GPIO端口
GPIOPinTypeGPIOOutput(LED_PORT, LED_PIN|LED_PIN1);//设置LED所在管脚为输出
GPIOPinWrite(LED_PORT, LED_PIN, 0xFF);//熄灭LED
}
/*看门狗初始化*/
void wdogInit(void) {
unsigned long ulValue = 350*(TheSysClock/1000);//准备定时350ms
SysCtlPeripheralEnable(SYSCTL_PERIPH_WDOG);//使能看门狗模块
WatchdogResetEnable(WATCHDOG_BASE);//使能看门狗复位功能
WatchdogStallEnable(WATCHDOG_BASE);//使能调试器暂停看门狗计数
WatchdogReloadSet(WATCHDOG_BASE, ulValue);//设置看门狗装载值
WatchdogIntEnable(WATCHDOG_BASE);//使能看门狗中断
IntEnable(INT_WATCHDOG);//使能看门狗模块中断
IntMasterEnable();//使能处理器中断
WatchdogEnable(WATCHDOG_BASE);//使能看门狗
WatchdogLock(WATCHDOG_BASE);//锁定看门狗
}
void delay(unsigned int num) {
unsigned int a;
a=num;
for(;a>0;a--);
}
void WatchdogFeed(void) { //喂狗程序
WatchdogUnlock(WATCHDOG_BASE);//解锁
WatchdogIntClear(WATCHDOG_BASE);//清除中断状态,重要!
WatchdogLock(WATCHDOG_BASE);//锁定
GPIOPinWrite(LED_PORT,LED_PIN,0x00);//LED_PIN 引脚输出0(点亮LED)
delay(10000);//延时
GPIOPinWrite(LED_PORT,LED_PIN,0xFF);//LED_PIN 引脚输出1(熄灭LED)
}
int main(void) {
clockInit();//时钟初始化:晶振,6MHz
ledInit(); //LED初始化
GPIOPinWrite(LED_PORT, LED_PIN1, 0x00);//熄灭LED
delay(100000);
GPIOPinWrite(LED_PORT, LED_PIN1, 0xFF);//熄灭LED
delay(100000);
wdogInit();//看门狗初始化
for (;;) {
WatchdogFeed();
delay(100000);//喂狗间隔可保证不会出现复位
}
}
(3)实验中存在的问题及处理方法
运行过程环境变量配置的问题
由于环境变量配置比较多,所以在这个环节往往会出现环境变量漏配置的问题或配置错误,这会使程序无法运行
解决办法:认真细心地一步一步地配置
4. 收获和体会
4. 思考题
1)如何得到其它时间(如:2s、3s等)的定时。
有如下语句:unsigned long ulValue = n*(TheSysClock/1000); 若要定时2s则令n=2000,同理若要定时3s则令n=3000。再用函数库void TimerLoadSet(unsigned long ulBase, unsigned long ulTimer, unsigned long ulValue);中的第三个参数来调用即可。
3) 计数器的初值是如何得到的,中断的注册是如何完成的。
根据需要可设置计数器的初值;中断的注册可通过另设置一中断服务程序(函数),可通过调用相关库函数并设置相关参数来完成注册。
实验3 PWM发生器实验
1. 了解Cortex-M3的PWM使用及其相关的API函数;
2. 掌握Cortex-M3的PWM使用的方法与步骤。
1. 实验原理
脉宽调制PWM(Pulse Width Modulation)是一项功能强大的技术,是一种对模拟信号电平进行数字化编码的方法。在脉冲调制中使用高分辨率计数器来产生方波,并且可以通过调整方波的占空比来对模拟信号电平进行编码。PWM通常使用在开关电源(switching power)和电机控制中。
Stellaris PWM模块由1到4个PWM发生器模块和1个控制模块组成。每个PWM发生器模块包含1个定时器(16位递减或先递增后递减计数器)、2个PWM比较器、PWM信号发生器(可产生8个独立的PWM信号)、死区发生器(可产生4对带死区延时的PWM信号)和中断/ADC触发选择器。而控制模块决定了PWM信号的极性,以及将哪个信号传递到引脚。
每个PWM发生器模块产生2个PWM信号。这2个PWM信号共享同一定时器和频率,可以编程为独立的信号,也可以编程为一对插入死区的延迟的互补(complement)信号。这些PWM发生模块的输出信号在传递到器件引脚之前,由输出控制模块管理。
配置PWM相关的控制参数,使能PWM外设,设定源时钟和比较寄存器以及死区寄存器,设置占空比,最后使能PWM发生器。
2. 实验内容
编程配置Cortex-M3的PWM功能参数,输出一定占空比的PWM波形。
(1) 硬件电路如下
(2) 程序如下:
① 系统初始化头文件systemInit.h
#ifndef __SYSTEM_INIT_H__
#define __SYSTEM_INIT_H__
/*包含必要的头文件*/
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
/*声明全局的系统时钟变量*/
extern unsigned long TheSysClock;
/*系统时钟初始化*/
extern void clockInit(void);
#endif // __SYSTEM_INIT_H__
② 系统初始化实现文件systemInit.c
#include "systemInit.h"
/*定义全局的系统时钟变量*/
unsigned long TheSysClock = 12000000UL;
/*定义KEY*/
#define KEY_PERIPH SYSCTL_PERIPH_GPIOB
#define KEY_PORT GPIO_PORTB_BASE
#define KEY_PIN GPIO_PIN_5
/*系统时钟初始化*/
void clockInit(void) {
SysCtlLDOSet(SYSCTL_LDO_2_50V); //设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC | //系统时钟设置
SYSCTL_OSC_MAIN | //采用主振荡器
SYSCTL_XTAL_6MHZ | //外接6MHz晶振
SYSCTL_SYSDIV_1); //不分频
TheSysClock = SysCtlClockGet(); //获取当前的系统时钟频率
}
③ 主函数mian.c
/*过程:上电全速运行程序,用示波器观察PWM0或PWM1插孔的波形*/
#include "hw_types.h"
#include "hw_memmap.h"
#include "hw_ints.h"
#include "hw_gpio.h"
#include "hw_pwm.h"
#include <sysctl.h>
#include "systick.h"
#include "debug.h"
#include "gpio.h"
#include "pwm.h"
#include "systeminit.h"
int main(void) {
clockInit();
SysCtlPWMClockSet(SYSCTL_PWMDIV_1);//PWM时钟源1分频
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);//使能PD口外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM);//使能PWM外设
/*设置PD0,PD1为PWM0和PWM1*/
GPIOPinTypePWM(GPIO_PORTD_BASE,GPIO_PIN_0|GPIO_PIN_1);
/*设置PWM发生器0为上下计数方式,两路PWM不同步 */
PWMGenConfigure(PWM_BASE,PWM_GEN_0,PWM_GEN_MODE_UP_DOWN|
PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM_BASE,PWM_GEN_0,60000);//设置两路PWM的共同周期
PWMPulseWidthSet(PWM_BASE,PWM_OUT_0,40000); //分别设置两路PWM的匹配值
PWMPulseWidthSet(PWM_BASE,PWM_OUT_1,20000); //修改50000和10000可以设置占空比
/*使能PWM0和PWM1*/
PWMOutputState(PWM_BASE,PWM_OUT_0_BIT|PWM_OUT_1_BIT,true); PWMGenEnable(PWM_BASE, PWM_GEN_0);//使能PWM发生器0
while(1);
}
4) 实验中遇到的问题及处理办法
参看实验二
3. 收获与体会
4.
5. 思考题
1)PWM发生器模块包括哪些部件,每个部件的作用是什么?
PWM发生器模块由以下部件组成:
(1) PWM定时器:用于计数(递减计数或先递增后递减计数);
(2) PWM比较器:每个PWM发生器含两个比较器,用于监控计数器的值。当比较器的值与计数器的值相等时,比较器会输出一个宽度为单个时钟周期的高电平脉冲;
(3) PWM信号发生器:作为高低电平驱动信号;
(4) 死区发生器:产生时间延迟;
(5) 中断/ADC触发选择器:产生中断或ADC触发信号。
2) PWM控制器模块的作用是什么?
PWM控制器模块决定了PWM信号的极性,以及将哪个信号传递到引脚。
3) Stellaris系列ARM的PWM模块可用于哪些场合?
Stellaris系列ARM的PWM模块通常使用在开关电源(switching power)和电机控制中。
实验三 PWM实验电路图
实验结果:
实验二
2- 1)计数器实验电路图
2- 2)秒时钟发生器实验电路图
2- 3)看门狗定时器实验电路图
实验4 16*16 LED点阵显示汉字实验电路图
1.学习Cortex-M3的GPIO使用及其相关的API函数;
2.掌握16*16 LED点阵显示汉字的原理及其控制方法。
1. 实验原理
该模块主要显示汉字的功能。采用两片74LS138控制行信号,两片74LS595传送数据,实现串入并出的的方式,这样可以节省MCU的GPIO口。74LS138控制的行给低电平,数据位给高电平的时候对应的点会点亮。
配置Cortex-M3的GPIO端口,用GPIO端口控制74LS138译码产生16位行选通信号,控制74HC595(串转并的方式)传送16位的显示数据。
2. 实验内容
用Cortex-M3的GPIO控制74LS138和74HC595,即控制行、列扫信号,来驱动16X16 LED点阵显示多个不同的汉字。
1) 硬件电路如下:
2) 程序如下:
(1) 系统初始化头文件systemInit.h
#ifndef __SYSTEM_INIT_H__
#define __SYSTEM_INIT_H__
/*包含必要的头文件*/
#include <hw_types.h>
#include <hw_memmap.h>
#include <hw_ints.h>
#include <interrupt.h>
#include <sysctl.h>
#include <gpio.h>
/*声明全局的系统时钟变量*/
extern unsigned long TheSysClock;
/*系统时钟初始化*/
extern void clockInit(void);
#endif // __SYSTEM_INIT_H__
(2) 系统初始化实现文件systemInit.c
#include "systemInit.h"
/*定义全局的系统时钟变量*/
unsigned long TheSysClock = 12000000UL;
/*定义KEY*/
#define KEY_PERIPH SYSCTL_PERIPH_GPIOB
#define KEY_PORT GPIO_PORTB_BASE
#define KEY_PIN GPIO_PIN_5
/*系统时钟初始化*/
void clockInit(void) {
SysCtlLDOSet(SYSCTL_LDO_2_50V); //设置LDO输出电压
SysCtlClockSet(SYSCTL_USE_OSC| //系统时钟设置
SYSCTL_OSC_MAIN| //采用主振荡器
SYSCTL_XTAL_6MHZ| //外接6MHz晶振
SYSCTL_SYSDIV_1); //不分频
TheSysClock = SysCtlClockGet(); //获取当前的系统时钟频率
}
(3) 主程序main.c
/*
接线:
1、用导线将MCU的MCU_IO1---MCU_IO4分别连接到16*16LED的A0、A1、A2、A3。
2、用导线将MCU的MCU_IO9---MCU_IO11分别连接到16*16LED的SHCR、STCP、DS。
3、将CPU板上的跳线帽JP1--JP8短接在左侧。
过程:
上电,编译、下载程序,观察16*16LED上显示的内容。
*/
#include "systemInit.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "hw_ints.h"
#include "sysctl.h"
#include "systick.h"
#include "hw_gpio.h"
#define uchar unsigned char
#define uint unsigned int
unsigned char table[] = {
0x01,0x00,0x00,0x80,0x3F,0xFE,0x20,0x80,0x2F,0xF8,0x20,0x88,0x3F,0xFE,0x20,0x88,
0x2F,0xF8,0x28,0x80,0x24,0xC8,0x22,0xD8,0x24,0xA0,0x58,0x98,0x4A,0x8E,0x81,0x04/*"康",0*/
};
#define LED_PERIPH SYSCTL_PERIPH_GPIOD |SYSCTL_PERIPH_GPIOA
#define LED_SHCP_BASE GPIO_PORTA_BASE // SHCP - PE0
#define LED_SHCP_PIN GPIO_PIN_0
#define LED_STCP_BASE GPIO_PORTA_BASE // STCP - PE1
#define LED_STCP_PIN GPIO_PIN_1
#define LED_DS_BASE GPIO_PORTA_BASE // DS - PE2
#define LED_DS_PIN GPIO_PIN_2
#define DATA_PORT_BASE GPIO_PORTD_BASE // DATA[7:0] - PD[7:0]
#define DATA_PORT_PIN 0xFF
/*硬件相关的定义*/
#define LED_SHCP_BASE_PIN LED_SHCP_BASE,LED_SHCP_PIN
#define LED_STCP_BASE_PIN LED_STCP_BASE,LED_STCP_PIN
#define LED_DS_BASE_PIN LED_DS_BASE, LED_DS_PIN
#define DATA_PORT_BASE_PIN DATA_PORT_BASE,DATA_PORT_PIN
/*硬件相关的位操作定义*/
#define LED_SHCP HWREG(LED_SHCP_BASE+(GPIO_O_DATA+(LED_SHCP_PIN<<2)))
#define LED_STCP HWREG(LED_STCP_BASE+(GPIO_O_DATA+(LED_STCP_PIN<<2)))
#define LED_DS HWREG(LED_DS_BASE +(GPIO_O_DATA + (LED_DS_PIN << 2)))
#define DATA_PORT HWREG(DATA_PORT_BASE+(GPIO_O_DATA+(DATA_PORT_PIN<<2)))
/*设置数据口为输出方向*/
#define DATA_PROT_OUTPUT_SET() HWREG(DATA_PORT_BASE+GPIO_O_DIR)= 0xFF
/*SHCP管脚为低*/
#define SHCP_L GPIOPinWrite(LED_SHCP_BASE ,LED_SHCP_PIN,0x00);
/*STCP管脚为低*/
#define STCP_L GPIOPinWrite(LED_STCP_BASE ,LED_STCP_PIN,0x00);
#define DS_L GPIOPinWrite(LED_DS_BASE , LED_DS_PIN,0x00);//DS管脚为低
/*SHCP管脚为高*/
#define SHCP_H GPIOPinWrite(LED_SHCP_BASE ,LED_SHCP_PIN,0xFF);//STCP管脚为高
#define STCP_H GPIOPinWrite(LED_STCP_BASE ,LED_STCP_PIN,0xFF);
#define DS_H GPIOPinWrite(LED_DS_BASE , LED_DS_PIN,0xFF);//DS管脚为高
void delay(unsigned int count) {
unsigned int a;
for(a=count;a>0;a--);
}
void init(void) {
clockInit();
SysCtlPeripheralEnable(LED_PERIPH); // 使能相应的GPIO
GPIOPinTypeGPIOOutput(LED_SHCP_BASE_PIN); // 设置相应的控制管脚为输出
GPIOPinTypeGPIOOutput(LED_STCP_BASE_PIN); // 设置相应的控制管脚为输出
GPIOPinTypeGPIOOutput(LED_DS_BASE_PIN); // 设置相应的控制管脚为输出
GPIOPinTypeGPIOOutput(DATA_PORT_BASE_PIN); // 设置相应的数据管脚为输出
/*配置数据端口为8mA,若上拉输出*/
GPIOPadConfigSet(DATA_PORT_BASE, LED_SHCP_PIN |
LED_STCP_PIN | LED_DS_PIN |DATA_PORT_PIN ,
GPIO_STRENGTH_8MA,GPIO_PIN_TYPE_STD_WPU);
DATA_PORT=0xFF;
SHCP_H;
STCP_H;
DS_H;
}
void WriteByte(unsigned char dat) { //写一个字节的数据
unsigned char i,x=0;
for(i=0;i<8;i++) { //循环8次把编码传给锁存器
x=dat&0x1
SHCP_L;
if(x==0) {
DS_L;
}else {
DS_H;
}
dat=dat>>1; //右移一位,取出该字节的最低位
SHCP_H;
}
}
void disp(unsigned char *p) {
unsigned int num,i,temp[32];
for(i=0;i<32;i++) {
temp[i]=*p++;
}
for(i=0;i<32;i++) {
for(num=0;num<16;num++) {
WriteByte(temp[2*num]); //送出一个字节
WriteByte(temp[2*num+1]);
STCP_L;//STCP_H;输出锁存器中的数据,下降沿
DATA_PORT=num; //行选
STCP_H;//STCP_L;
delay(1000);
}
}
}
int main(void) {
init();
while(1) {
disp(table); //显示“康”
}
}
3) 实验中存在的问题及处理方法
参考实验二
3. 收获和体会
4. 思考题
1. 简述LED点阵显示汉字的原理。
该模块主要显示汉字的功能。采用两片74LS138控制行信号,两片74LS595传送数据,实现串入并出的的方式,这样可以节省MCU的GPIO口。74LS138控制的行给低电平,数据位给高电平的时候对应的点会点亮。
武汉大学计算机学院08级嵌入式系统设计实验报告班级08级计科5班指导老师武小平学期20xx20xx第1学期小组成员目录一设计题目选…
嵌入式系统实验报告姓名:##学号:##班级:计算机科学与技术非师范121班实验一LCD控制一、实验目的1、初步掌握液晶显示屏的使用…
嵌入式实验报告姓名邓庆学号1075490019学院通信工程学院实验一电源复位时钟管理单元一实验目的1了解如何配置不同的时钟2如何使…
嵌入式系统实验报告学院测量与通信工程学院专业信号与信息处理学生姓名姜元学号1320xx0050指导教师董静薇一实验目的了解Boot…
嵌入式系统实验报告小组成员指导老师实验三键盘及LED驱动实验一实验目的1学习键盘及LED驱动原理2掌握ZLG7289芯片的使用方法…
这学期才接触嵌入式系统感觉还称不上入门,我通过学习知道了嵌入式的发展前景很大,各个领域都用到了嵌入式,学好嵌入式不愁没饭吃。广义上…
嵌入式心得体会嵌入式心得体会一嵌入式学习心得体会4月10号为期一个阶段的Linux开发基础培训课程圆满结束回首这些天所留下的点点滴…
嵌入式系统原理实验总结报告车辆座椅控制系统实验20xx523嵌入式系统原理实验总结报告一技术性总结报告一题目车辆座椅控制系统实验二…
中国地质大学北京实验报告课程名称嵌入式系统实验名称嵌入式LinuxSocket编程姓名杨森学号1010102115班级101010…
嵌入式系统设计实验报告班级学号姓名成绩指导教师1实验一11实验名称博创UP3000实验台基本结构及使用方法12实验目的1学习嵌入式…
嵌入式实验报告期末论文学生姓名杨佳洁学号11570118班级11计算机2班指导教师黄静20xx年5月20日嵌入式程序设计与应用课程…