从51单片机到STM32再到TM4C123
目录 系统时钟 GPIO相关 通用定时器相关 PWM相关 UART通信相关
写在前面: 进入TI的学习,说明STM32 已经掌握的差不多了,没有掌握的很好也没关系,正好两块单片机对比着学习更能加深映像。这篇只是简单的介绍TI,篇幅比较短比,主要还是叙述了32和TI的不同的地方,相似的就没有再写出来,不能完全的拿来当作学习资料。
一点学习TI的建议: TI主要学习还是通过北航出版的《嵌入式系统教程——基于TivaC系列 ARM Cortex—M4微控制器》为主资料,因为这个资料比较通俗易懂,如果不喜欢看电子版的,可以去图书馆借这本书,还有一本配套的书,忘记叫啥名字了,但是想借的话直接图书馆检索TM4C123GH6PM就可以找到,这本书主要还是纯文字教你怎么配置相应功能和相应项目的思路。其次,除了北航的那本书,配套的资料还应该有《TM4C1233H6PM数据手册中文版》、中英文版本的《TM4CDriver_Library》,数据手册的话主要就是了解相关外设功能,以及对应映射引脚查找。《TM4CDriver_Library》主要是API文档,因为TI不同于32,TI的函数的入口参数无法通过像32一样的方法查找选择合适的参数,所以,该文档就很有作用,对于一个函数,可以通过该文档进行查找,找到函数所在的位置,有详细的关于该函数的讲解,可以了解该功能,同时还有入口参数的例子,若要选择其他入口参数,则复制该参数,在程序文件里面查找定义的位置,就可以看到附近还有同类的参数,选择需要的参数即可。 对于单片机的定时,那是一部分很重要的东西,这其中就有以分资料单独讲怎么配置各种定时器,实现不同功能,有例程,《TM4C123G定时器》就是一份资料,学习定时器的时候对照着来,会比较容易很多。 当然了,网上还有很多人的学习记录博客,这些也是很有用的资料,因为学习TI,就是要知道怎么用,怎么配置相关程序,博客就解决了这类问题。CSDN上面这一类型的TI教程不多,懒得找的话可以来找我拷贝。
系统时钟对于51单片机来说,在书写程序时候是不需要进行系统的时钟函数的配置的,直接由晶振电路来提供。
如图所示,直接是32和TI的系统时钟框图 32系统时钟 TI系统时钟
对于32 的时钟来说,常用的时钟主要分为AHB和APB两大部分,且系统时钟不需要自己来配置。 /* void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphClockCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); */ 每个部分对应多大的频率,对应的定时器编号,串口编号也已经设定好。所以,在使用相对应的功能时候直接在这两类里面进行查找然后使能外设就可以用了,例如使用GPIO、定时器等功能时候直接用函数里面的参数来查找:
/* RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能串口 */ 然而,对于TI来说,系统的时钟也是分为AHB和APB两种,但是在配置时候可进行自由选择(由于初入TI,对这里没有仔细学习,感兴趣的可以了解),但是配置时钟时候,可以只需要运用“SysCtlClockSet()”函数来自己来设定,不像32一样直接规定了不同外设用不同的频率。函数的入口参数就是:系统的分频、使用OSC还是PLL、OSC时钟源选择、外接晶体频率,这四种来完成设备的时钟,例如: /* SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_XTAL_16MHZ SYSCTL_OSC_MAIN) */ 最后使能相应的功能即可。(关于其时钟配置的参数详细讲解见)。 在配置好系统的时钟之后,根据需要的外设功能进行相应的使能: /* SysCtlPeripheralEnable(uint32_t ui32Peripheral) */ 其中的入口参数为SYSCTL_PERIPH_GPIOA、SYSCTL_PERIPH_PWM1、SYSCTL_PERIPH_TIMER1等等。 注意:32的系统频率的配置是固定的,定在了不同的总线的使能函数,所以不同的外设可以说是“不共用”系统时钟的,而TI是先于外设使能前进行自己时钟配置,然后才使能所需外设,且“公用”系统时钟。 GPIO相关说完时钟和系统配置,下面简绍基本的GPIO口的配置。
GPIO的功能大致上还是和32的类似,可输出高低电平、UART通信、还可以模拟一些简单的通信接口,如SPI,IIC等。一般工作模式有以下几种: 浮空输入 上拉输入 下拉输入 模拟输入 开漏输出 推挽输出 复用推挽输出 复用开漏输出
一般GPIO在复位之后都有一个默认的方向,为了安全起见,都是默认为输入模式,废话不多说,直接进入配置: 首先是32的配置 /* void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_Init(GPIOE, &GPIO_InitStructure); } */ 其次是TI的配置: /* SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //使能GPIOF外设 GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3); */
不难看出,配置32和TI的方式就是不一样的,32的GPIO是用结构体进行配置,TI则使用函数来进行配置。TI的普通配置只需要进行GPIO外设使能和方向控制即可。也不是说前面说到的功能模式就不能配置了,如果有需要可以进行其他类型的配置。 如果要进行端口高低电平的输出控制,32和TI也是一样的调用函数来进行配置,分别是:GPIO_SetBits(GPIOE,GPIO_Pin_5); 和 GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0);
注意:TI的输出高低电平使用的是同一个函数GPIOPinWrite(),对于32则使用的是两个函数:GPIO_SetBits()和GPIO_ResetBit()即可输出高低电平。TI呢,则不是这样的,就连入口参数也不是和32一样那么简单,GPIOPinWrite()入口参数就有三个,分别是GPIO_PORTx_BASE,GPIO_PIN_x,和0(低电平时候为0,但是高电平时候不是1,而是GPIO_PIN_x)。例如PF1输出高电平 : GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
如果想进行其他端口详细的配置情况可以见相关手册资料,其中端口复用放在后面各功能实现时候讲。 通用定时器相关TI定时器简介: TI定时器呢,主要分为12个大的定时器模块(GPTM),每个定时器模块又分为定时器A和定时器B,所以总共有24个定时器。这12个大的定时器的属性分为16/32位的GPTM块和32/64位块,其中各6块。每个16/32位的定时器块,提供2路16位的定时器/计数器(即定时器A和定时器B),所以通俗来说就是定时器A或B单独配置时候就是12个16位的定时器,级联起来配置就是6个32位的定时器。同样的,32/64位的块就是12个32位的定时器,级联时候就变成6个64位的定时器。 功能说明: 每个 GPTM 模块的主要元件包括两个自由运行的递增/递减计数器(称作 Timer A 和 Timer B)、两个预分频器寄存器、两个匹配寄存器、两个预分频器匹配寄存器、两个影子寄存器、两个加载/初始化寄存器以及与它们相关的控制功能。 接着来,该单片机的定时器和32的有点不同,特别是计数控制计数的寄存器。配置了32的定时器之后,定时器开始计数,当计数达到重装载值时候产生中断,之后会重新“自动”装载计数值,但是Ti却并非如此,是否自动装载计数值是需要在配置时候进行设置的,官方一点的来说就是:单次触发/周期定时器模式。 相关定时器的功能如下: 接着进入配置对比: 首先还是先32的配置: /* TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler =psc; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); */ TI的配置: /* //TimerConfigure(TIMER0_BASE, TIMER_CFG_ONE_SHOT);//单次计数模式 TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);//周期性计数模式 TimerLoadSet(TIMER0_BASE, 2222222- 1);//设定预装栽值 IntEnable(INT_TIMER0A); //使能TIMER0A TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); //TIMEOUT标志位触发中断 //TIMER0A开始计数,当计数值等于TimerLoadSet,触发中断 */ 同GPIO口的配置一样的,32的配置还是结构体类型的配置,TI还是调用函数进行配置。 PWM模式: 通用定时器支持简单的PWM生成模式。在 PWM 模式中,定时器被配置为 24 位或 48 位递减计数器,也就是说在定时器里面就有这个功能,配置好以后就直接输出PWM,而不像32一样配置好定时器之后还要一个TIM_SetCompare1()函数来进行PWM的输出。 注意:除了前面介绍的不同点之后,与32还有一个很大的区别就是需要进行中断的执行相关功能时候,需要自己调用TimerIntRegister()函数来注册中断函数的名字,这样中断才有入口。如果不需要忽略即可。同时在使能中断时候,和32一样需要开启一些中断。 PWM相关PWM简介: TI有2个PWM模块,每个模块由4个PWM发生器模块和一个控制器模块组成,一共可以产生16个PWM输出。每个发生器模块都能够产生2个PWM信号,这两个信号是基于同一个定时器和频率,也可以配置成单独产生信号。PWM模块发生器产生的两个信号为PWMA和PWMB。 以下为结构图: 功能描述: 每个PWM发生器中都有区别与通用定时器的定时器,在向下计数模式或向上/向下计数模式。 每个PWM发生器都有两个比较器。具体功能和32一样就不再累赘。 初始化及配置:
首先还是32的: /* TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC2Init(TIM3, &TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); */
其次是TI的:
/* //单独配置的系统时钟 SysCtlPWMClockSet(SYSCTL_PWMDIV_1); SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); //配置引脚为PWM功能 syslPeripheralEnable(SYSCTL_PERIPH_GPIOF); GPIOPinConfigure(GPIO_PF3_M1PWM7); GPIOPinTypePWM(GPIOF, Pin3); //配置 PWM1 Generator3·发生器 PMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN|PWM_GEN_MODE_NO_SYNC); //配置 PWM1 Generator3 周期 PWenPeriodSet(PWM1_BASE, PWM_GEN_3, SysCtlClockGet()/freq - 1); //配置 PWM1 Generator3·占空比 PMPulseWidthSet(PWM1_BASE, PWM_OUT_7, PWMGenPeriodGet(PWM1_BASE, PWM_GEN_3)*duty - 1); //使能PWM1的输出 PWOutputState(PWM1_BASE, PWM_OUT_7_BIT, true); //使能 PWM1 发生器模块 PMGenEnable(PWM1_BASE, PWM_GEN_3); */ 还是和前面一样,32的PWM配置过程是结构体类型的,而TI则还是库函数来进行配置的。有一点值得注意哦,TI在配置PWM时候不需要进行IO口的复用配置和映射等相关操作,只要找到对应的IO口就可以输出信号。 UART通信相关微控制器的UART简介: TI控制器配备了多达8路的UART,其基本功能和32差不多,具体功能可以参考32相关文档
这部分我们先来看32和TI的配置,从配置中找不同的地方加以说明
首先是32配置: /* USART_InitStruct.USART_BaudRate=arr; USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode=USART_Mode_Tx | USART_Mode_Rx; USART_InitStruct.USART_Parity=USART_Parity_No; USART_InitStruct.USART_StopBits= USART_StopBits_1; USART_InitStruct.USART_WordLength= USART_WordLength_8b; USART_Init(USART2, &USART_InitStruct);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART_Cmd(USART2, ENABLE); */ 32的配置过程就不加以叙述
TI的配置: /* SCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); //使能GPIO SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);//使能UART0 GPIOPinConfigure(GPIO_PA0_U0RX); //复用GPIO GPIOPinConfigure(GPIO_PA1_U0TX);· GPIOPinTypeUART(GPIO_PORTA_BASE,GPIO_PIN_0|GPIO_PIN_1);// UARTClockSourceSet(UART0_BASE,UART_CLOCK_PIOSC);//选择串口时钟来源 UARTStdioConfig(0,115200,16000000);//选择模式、波特率、和时钟大小 */
从配置上可以看出,32和TI的 配置都是需要将UART的端口映射到对应的GPIO口上,当然了,配置过程依旧是32为结构体,TI为调用库函数,但是在配置时候,32还要进行相关NVIC中断配置,设置中断优先级,设置停止位等等。TI的配置就是以上那些函数,其他的功能都是默认,如果需要可以进行自行查找配置。
这里还涉及到了端口复用。TI的端口复用很简单,不用像32一样先进行各种IO口的配置,直接用函数SCtlPeripheralEnable() 使能相关IO口的时钟,GPIOPinConfigure() 函数里选择相应复用的功能即可完成配置。 总的来说复用是在你要使用的外设功能函数里面进行的,不需要像32一样拿出来进行配置来使用。 基于UART通信等相关知识点,可以先熟悉32的通信知识点后来使用TI,则会变得容易许多。
Word格式文档51黑下载地址: |