写在前面:这几篇主要是作为笔记用,所以写的非常简陋…(或者说,乱orz),现在由于对markdown的语法也不太熟悉,而且TM4也才刚刚接触,非常可能会出现有错的地方…!
如果不嫌弃的话欢迎指教!!(qq:1159806228)
这几篇有望在19年国赛结束后慢慢整理完毕(。
一、串口
需要引用库:
#include <stdbool.h>
#include <stdint.h>
#include “inc/hw_gpio.h”//gpio宏定义
#include “inc/hw_memmap.h”//rom宏定义remap
#include “inc/hw_types.h”//板子类型宏定义(?)
#include “inc/hw_ints.h”//中断宏定义
#include “driverlib/gpio.h”//gpio库
#include “driverlib/pin_map.h”//gpio功能映射
#include “driverlib/rom.h”
#include “driverlib/rom_map.h”//关于rom的一些库函数remap
#include “driverlib/sysctl.h”//时钟相关
#include “driverlib/uart.h”//串口相关
#include “driverlib/interrupt.h”//中断相关
#include “pinout.h”//这个是我用TI的PinMux生成的初始化IO口文件(用法类似于stm32cube)
IO口配置(这些也可以用PinMux生成.c文件自动配置好):
//使能PORT
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//使能串口0
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
//配置IO口功能
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_0 | GPIO_PIN_1);
//选择串口时钟:内部精确时钟/系统时钟
UARTClockSourceSet(UART0_BASE,UART_CLOCK_PIOSC);
//(不推荐!!这个是
看examples里的,但是调用的不是官方库,而且校验停止什么的都没选,感觉不是很好,不如下面那个)
//配置串口:串口编号/波特率/时钟频率
UARTStdioConfig(0, 115200,16000000);
以下这些不包含在PinMux生成的.c文件里,所以下面这些是必须需要的啊!!!!!!!!!)
(顺便PinMux党记得还要添加 SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);语句(使能串口0时钟…不知道为啥它没有自动写这句QAQ))
(↑惨痛的教训)
//配置串口:
//串口号/时钟/波特率/格式
UARTConfigSetExpClk(UART0_BASE,ROM_SysCtlClockGet(),115200, (UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE |UART_CONFIG_PAR_NONE));
//开串口中断(使能&发送接收中断)
//注册UART0中断服务函数,第二个是中断函数句柄
UARTIntRegister(UART0_BASE,UART0IntHandler);
// 设置中断优先级,TM4C123G的中断优先级有8个,0最高
IntPrioritySet(INT_UART0,0);
//使能接收完成中断和接收超时中断
UARTIntEnable(UART0_BASE,UART_INT_RX | UART_INT_RT);
//使能UART0中断
IntEnable(INT_UART0);
接收和发送
采用了FIFO模式接收,因为串口速率不及CPU,用了FIFO减少了CPU频繁去操作串口数据的负担。我自己测了测是8个字节才会触发一次UART_INT_RX状态,所以如果需要实时性很高,又要每次只传一个字节的话,那就不要用检测串口状态来接收数据了(不然你就得等8次才能收一次数据),而是通过检测FIFO有无数据来接收数据。
用到了下面这些函数:
·
UARTCharsAvail(UART0_BASE)//检测FIFO有无数据
UARTCharGet(UART0_BASE);//接收一字节数据
UARTCharPut(UART0_BASE,data);//发送一字节数据
//下面是一个自收发例子(这个是参考了别人的代码,用到了FIFO,所以电脑发送8个字节单片机才会接收一次)
void UART0IntHandler(void)
{
//获取中断标志
uint32_t flag =UARTIntStatus(UART0_BASE,1);
//清除中断标志
UARTIntClear(UART0_BASE,flag);
if(flag&UART_INT_RX)//接收中断
//UARTCharsAvail()判断FIFO是否还有数据
while(UARTCharsAvail(UART0_BASE))
{
//输出得到的数据
UARTCharPut(UART0_BASE,UARTCharGet(UART0_BASE));
}
}
我是这么写的(一个字节接收一次,所以没有用flag检测串口状态)
void UART0IntHandler(void)
{
//获取中断标志
uint32_t flag = UARTIntStatus(UART0_BASE,1);
UARTIntClear(UART0_BASE,flag);
uint8_t data;
while(UARTCharsAvail(UART0_BASE))
{
/* 接收数据 */
data = UARTCharGet(UART0_BASE);
/* 将数据发送出去 */
UARTCharPut(UART0_BASE, data);
}
}
二、中断
中断部分参考了https://blog.csdn.net/qq_43725844/article/details/89093405
大部分GPIO都可以做中断,中断是以一个port为一组的(同一个port下io口共用一个中断函数),在中断函数里检测是哪个io被触发。
(…QAQ虽然和中断没什么关系…但是记得launchPad上按键没有电容记得软件消抖啊(惨痛的教训+1
需要引用库:
#include<stdint.h>
#include<stdbool.h>
#include“ inc/hw_memmap.h”
#include “inc/hw_ints.h”
#include “driverlib/gpio.h”
#include “driverlib/pin_map.h”
#include “driverlib/sysctl.h”
#include “driverlib/interrupt.h”
中断配置过程:(以launchPad上SW1为例)
//IO中断口使能
GPIOIntEnable(GPIO_PORTF_BASE,GPIO_PIN_4);
//IO中断配置
GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_FALLING_EDGE);
// 设置中断优先级,TM4C123G的中断优先级有8个,0最高
IntPrioritySet(INT_GPIOF|INT_GPIOP4, 0);
//中断注册,第二个参数是中断服务函数句柄
GPIOIntRegister(GPIO_PORTF_BASE,IntHandler_GPIOF);
//Port中断使能
IntEnable(INT_GPIOF);
//开启总中断
IntMasterEnable();
中断服务函数:
用到函数:
//获得指定的中断状态并返回;
ui32IntStatus= GPIOIntStatus(GPIO_PORTF_BASE,true);
//清除中断标志
GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
举个例子:流程如下:
void IntHandler_GPIOF(void)//中断服务函数
{
ui32 IntStatus = GPIOIntStatus(GPIO_PORTF_BASE, true);//获取状态
GPIOIntClear(GPIO_PORTF_BASE, GPIO_PIN_4);//清除指定的中断源
if((ui32IntStatus& GPIO_PIN_4) == GPIO_PIN_4)//这里可以判断是哪个io口的中断
{
//内部逻辑
}
}
三、定时器
TM4一共有两种定时器,16位和32位的(TIM和WTIM),每种有6组,每组下又有2个定时器(A、B)。每组两个定时器可以连起来扩展成两倍位宽的定时器,支持上升下降单次循环计数,也可以来产生PWM信号。
这里以TIM0的A定时器为例。
引用diverlib下timer.h文件,以后要用什么外设直接引用对应文件就可以,之后就不专门提了…
定时器流程:
/* 设置中断优先级,TM4C123G的中断优先级有8个,0最高 */
IntPrioritySet(INT_TIMER0A, 0);
SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
//系统时钟设置
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
//使能定时器模块Timer0。
TimerConfigure(TIMER0_BASE,TIMER_CFG_PERIODIC);
//配置定时器模块Timer0为Periodic周期性计数模式。
TimerLoadSet(TIMER0_BASE,TIMER_A,SysCtlClockGet()-1);
//TimerLoadSet(TIMER0_BASE,TIMER_A,SysCtlClockGet()-1); //定时1s
//设定定时器模块Timer0的Load重装载值为系统时钟频率的一半再减一(为0.5秒)。
TimerIntRegister(TIMER0_BASE,TIMER_A,IntHandle_TIMER0A);
//注册中断服务函数,最后那个是中断服务函数句柄,其实后面看库函数手册这一步也可以不用啦,那时候中断入口函数名字就是官方默认的(具体是什么忘了...)
IntEnable(INT_TIMER0A);
//使能定时器模块Timer0的定时器A的中断。
TimerIntEnable(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
//使能单独的定时器中断源,第一个TIMER0_BASE为Timer0的基地址,第二个是中断源启用的中断事件的标识码,TIMER_TIMA_TIMEOUT的意思是定时器A(TimerA)溢出(重装载),以此为标志位,当TimerA重装载时就会触发中断。
IntMasterEnable();
//使能处理器中断,使处理器能够响应中断。
TimerEnable(TIMER0_BASE,TIMER_A);
//使能定时器TimerA。
中断服务函数:
void IntHandle_TIMER0A(void)//和你注册中断句柄时的名称保持一致
{
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
//清除标志位,第二个是中断类型,我这里是定时器A溢出中断
{
//其他逻辑
}
}