13
2021
08

TI-TM4C123x系列单片机编程救急宝典(草稿版本,持续更新中。。。)


前言


TI-TM4C123x系列单片机的编程有种面向过程的感觉,无论是时钟设置,端口配置,还是操作外设都是采样的函数调用方式。这种方式有很强的结构化感觉,也便于初学者学习。下面作者将从多个角度介绍。

本手册更接近应用,跳过了大多的理论和原理,大篇幅讲述如何快速入门和使用起来该单片机,作者更建议读者认真阅读每一行,研究每一行的意义。最后,因作者能力有限,行文仓促未经复阅,文中难免出现错误,如您对内容有疑问可以与我联系!

作者:陈语(ChanRa1n) 高级软件设计师、高级项目管理师

邮箱:chenyu@myfpga.cn

网站:https://www.myfpga.cn/

 

目录

前言    1

函数存储    2

系统时钟    2

SysCtlClockSet()    2

SysCtlPeripheralEnable()    2

SysCtlClockGet()    2

SysCtlDelay()    2

GPIO    2

GPIOPinTypeGPIOOutput(PIN_GROUP,PIN)    2

GPIOPinWrite(PIN_GROUP,PIN,LEVEL)    2

GPIOPinTypeGPIOInput(PIN_GROUP,PIN)    2

GPIOPinRead(PIN_GROUP,PIN)    2

扩展(可以跳过)▼    2

点亮LED灯示例    2

PWM    2

SysCtlPWMClockSet()    2

SysCtlPeripheralEnable()    2

GPIOPinTypePWM()    2

PWMGenConfigure()    2

PWMGenPeriodSet ()    2

PWMPulseWidthSet ()    2

PWMOutputState ()    2

PWMGenEnable ()    2

串口    2

串口初始化示例    2

UARTStdioConfig()    2

UARTCharsAvail()    2

UARTCharGet()    2

UARTCharGetNonBlocking()    2

UARTCharPut()    2

UARTCharPutNonBlocking ()    2

小结    2

串口使用示例    2

中断    2

定时器    2

ADC    2

IIC/SPI    2

FPU    错误!未定义书签。

PIMMUX    错误!未定义书签。

关键结构    2

include头文件    2

 


函数存储


Many Tiva devices have portions of the peripheral driver library stored in an on-chip ROM. By using the code in the on-chip ROM, more flash is available for use by the application. The boot loader is also contained within the ROM, which can be called by an application in order to start a firmware update.

函数在TI-TM4C123x中有两种存储方式,一种是直接存放在Flash,另外一种是存放在片上ROM里面,这种结合的方法能够最大化利用Flash空间,将不频繁调用的函数如各种初始化等只运行一次且不影响功能的函数放在ROM里面存储。

所以你需要在文件头中引用映射库:

#include "driverlib/rom_map.h"

里面大概是这样的:

#ifdef ROM_ADCSequenceDataGet

#define    MAP_ADCSequenceDataGet    \

ROM_ADCSequenceDataGet

#else

#define    MAP_ADCSequenceDataGet    \

ADCSequenceDataGet

#endif

#define是宏定义,一般用来定义固定的参数,例如#define a 1代表定义一个常量a,值为1。

#ifdef是判断一个宏定义是否存在,如果成立则运行后面紧跟着的内容,否则运行#else中的内容。

代码中的

#define    MAP_ADCSequenceDataGet    \

ROM_ADCSequenceDataGet

#define    MAP_ADCSequenceDataGet    ROM_ADCSequenceDataGet

是等效的,\代表紧下一行中内容仍为本行内容,方便显示。

假设此处ROM_ADCSequenceDataGet被定义了,则调用MAP_ADCSequenceDataGet()函数时则调用ROM_ADCSequenceDataGet()函数,否则调用ADCSequenceDataGet()函数,名字中带有ROM_前缀的一般为存放当ROM中的函数,带有MAP_前缀的代表映射函数,会根据具体的宏定义去决定使用FLASH或者ROM中的程序。


系统时钟



SysCtlClockSet()


SysCtlClokcSet函数适用于系统时钟设置,只需要一条函数就可以实现时钟输入,分频倍频,晶振频率设置。例如:

SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |

SYSCTL_OSC_MAIN);

其中SYSCTL_SYSDIV_4代表4分频,SYSCTL_USE_PLL 代表使用PLL信号源,晶振频率为16Mhz,主振荡器作为OSC来源。

其他的参数选择可以参考#include "driverlib/sysctl.h"中的注释,例如:

#define SYSCTL_SYSDIV_1         0x07800000  // Processor clock is osc/pll /1

#define SYSCTL_SYSDIV_2         0x00C00000  // Processor clock is osc/pll /2

#define SYSCTL_SYSDIV_3         0x01400000  // Processor clock is osc/pll /3

#define SYSCTL_SYSDIV_4         0x01C00000  // Processor clock is osc/pll /4

#define SYSCTL_SYSDIV_5         0x02400000  // Processor clock is osc/pll /5

#define SYSCTL_SYSDIV_6         0x02C00000  // Processor clock is osc/pll /6

#define SYSCTL_SYSDIV_7         0x03400000  // Processor clock is osc/pll /7

#define SYSCTL_SYSDIV_8         0x03C00000  // Processor clock is osc/pll /8

#define SYSCTL_SYSDIV_9         0x04400000  // Processor clock is osc/pll /9

#define SYSCTL_SYSDIV_10        0x04C00000  // Processor clock is osc/pll /10

#define SYSCTL_SYSDIV_11        0x05400000  // Processor clock is osc/pll /11

#define SYSCTL_SYSDIV_12        0x05C00000  // Processor clock is osc/pll /12

时钟还可以选择为SYSCTL_SYSDIV_12等。


SysCtlPeripheralEnable()


SysCtlPeripheralEnable()函数类似STM32中的端口使能函数,它同样被定义在sysctl.h,使能函数也只有一行。例如:

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

打开了GPIOF的端口时钟。


SysCtlClockGet()


SysCtlClockGet()可以获取系统频率,值得注意的是它默认被放在ROM里面,所以习惯使用MAP_ SysCtlClockGet()是更正确的选择,具体的定义为:

#if defined(TARGET_IS_TM4C123_RA1)     ||    defined(TARGET_IS_TM4C123_RA3)    ||    defined(TARGET_IS_TM4C123_RB1)     ||    defined(TARGET_IS_TM4C123_RB2)

#define    ROM_SysCtlClockGet    ((uint32_t (*)(void))ROM_SYSCTLTABLE[24])

#endif

 


SysCtlDelay()


SysCtlDelay()配合SysCtlClockGet()可以实现精确的延时,常见用法为:

SysCtlDelay(SysCtlClockGet() / 10 / 3);

其中SysCtlClockGet()获取的是系统频率,除以10代表单片机0.1秒能够执行的单指令数目,而SysCtlDelay()每运行一次使用3个机器周期,所以如果希望延时0.1秒,则需要再除以3,就得到了SysCtlClockGet() / 1000 / 3。

同理,如果你需要延时1ms,则

SysCtlDelay(SysCtlClockGet() / 1000 / 3);

 


GPIO


GPIO相关的函数有模式配置,电平控制,电平读取,ADC参考后续ADC章节。为方便初学者理解,以下使用PIN_GROUP和PIN代替GPIO基址加偏移代表对应端口的方式。例如PIN_GROUP可以为:GPIO_PORTF_BASE,即GPIOF。


GPIOPinTypeGPIOOutput(PIN_GROUP,PIN)


本函数配置PIN_GROUP的PIN端口为输出模式,默认的输出强度为2mA,满足大多数情况的需求。示例:

MAP_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

 


GPIOPinWrite(PIN_GROUP,PIN,LEVEL)


本函数配置PIN_GROUP的PIN端口为LEVEL电平。示例:

GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2,1);//令F2端口为高电平1

 


GPIOPinTypeGPIOInput(PIN_GROUP,PIN)


本函数配置PIN_GROUP的PIN端口为输入模式。

 


GPIOPinRead(PIN_GROUP,PIN)


本函数读取PIN_GROUP的PIN端口电平状态,返回0低电平或者1高电平。示例:

GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2,1);


扩展(可以跳过)▼


GPIOPadConfigSet()

 

SysCtlPeripheralReady()

    

GPIODirModeSet()

 


    点亮LED灯示例


//代码使用TI-TM4C123x TIVA平台开发板

#include <stdint.h>

#include <stdbool.h>

#include "inc/hw_memmap.h"

#include "inc/hw_types.h"

#include "driverlib/fpu.h"

#include "driverlib/gpio.h"

#include "driverlib/pin_map.h"

#include "driverlib/rom.h"

#include "driverlib/rom_map.h"

#include "driverlib/sysctl.h"

 

int main(void)

{

//MAP_FPULazyStackingEnable();使能FPU后进行浮点数运算可以减轻CPU负担

MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |

SYSCTL_OSC_MAIN);

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

MAP_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

while(1)

{

GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);

SysCtlDelay(SysCtlClockGet() / 2 / 3);

GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);

SysCtlDelay(SysCtlClockGet() / 2 / 3);

}

}


PWM


TI-TM4C123x可以进行简单的PWM配置,快速输出需要的波形。相关的函数如下:


SysCtlPWMClockSet()


本函数配置用于PWM的时钟,示例:

SysCtlPWMClockSet(SYSCTL_PWMDIV_1);

即使用系统时钟1分频(不分频)作为时钟输入。PWM时钟应和需要输出的PWM频率相关,输出越低频率的PWM时使用越大的分频。

 


SysCtlPeripheralEnable()


上面讲过,该函数用于开启时钟,此处在PWM中使用的示例:

SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);

开启了PWM0的时钟。

    


GPIOPinTypePWM()


该函数用于指定在某个端口输出PWM,使用示例:

GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_3);

 


PWMGenConfigure()


该函数可以配置PWM的计数模式(向上计数和向下计数),示例:

MAP_PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN |

PWM_GEN_MODE_NO_SYNC);

 


PWMGenPeriodSet ()


PWMGenPeriodSet ()指的是对PWM的频率进行设置,准确的说,设置的是一个16位的寄存器,这个寄存器用来保存当前的计数值,以实现PWM的输出。示例:

MAP_PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, (SysCtlClockGet() / 250));

此处的250为最终输出的频率为250hz,SysCtlClockGet()代表一秒内可以执行的单周期指令数,而250hz的PWM每个占用的单周期指令数为SysCtlClockGet()/250,其他的也类似。

值得注意的是,保存计数值的寄存器位数为16位,这就是为什么上面PWM的分配比需要与输出的PWM频率有关。当PWM频率过小时,需要记的数就比较大,可能会超过16位寄存器所能表示的范围,导致SysCtlClockGet()/需要的频率的计算可能会发生溢出,导致产生不理想的PWM波形。

 


PWMPulseWidthSet ()


该函数用于指定PWM的占空比,例如:

MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,

MAP_PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) / 4);

函数指定了PWM的占空比为100%/4=25%。

当然,你也可以使用这样的方式直接指定占空比:

MAP_PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,

MAP_PWMGenPeriodGet(PWM0_BASE, PWM_GEN_0) *duty-1);

 


PWMOutputState ()


使能PWM输出信号,示例:

MAP_PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true);

 


PWMGenEnable ()


启动PWM发射器模块,示例:

MAP_PWMGenEnable(PWM0_BASE, PWM_GEN_0);

 


串口



串口初始化示例


void     ConfigureUART(void)

{

//

// Enable the GPIO Peripheral used by the UART.

//

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

 

//

// Enable UART0.

//

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

 

//

// Configure GPIO Pins for UART mode.

//

MAP_GPIOPinConfigure(GPIO_PA0_U0RX);

MAP_GPIOPinConfigure(GPIO_PA1_U0TX);

MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

 

//

// Use the internal 16MHz oscillator as the UART clock source.

//

UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

 

//

// Initialize the UART for console I/O.

//

UARTStdioConfig(0, 115200, 16000000);

}

首先开启了串口和串口对应GPIO的时钟,接下来将对应的GPIO引脚模式设置为串口收发,紧接着配置时钟,设置串口参数。

这里值得注意的是UARTStdioConfig函数中的第三个参数为系统频率,建议使用宏定义定义整体频率,方便系统移植和修改。


UARTStdioConfig()


//*****************************************************************************

//

//! Configures the UART console.

//!

//! \param ui32PortNum is the number of UART port to use for the serial console

//! (0-2)

//! \param ui32Baud is the bit rate that the UART is to be configured to use.

//! \param ui32SrcClock is the frequency of the source clock for the UART

//! module.

//!

//! This function will configure the specified serial port to be used as a

//! serial console.  The serial parameters are set to the baud rate

//! specified by the \e ui32Baud parameter and use 8 bit, no parity, and 1 stop

//! bit.

//!

//! This function must be called prior to using any of the other UART console

//! functions: UARTprintf() or UARTgets().  This function assumes that the

//! caller has previously configured the relevant UART pins for operation as a

//! UART rather than as GPIOs.

//!

//! \return None.

//

//*****************************************************************************

void UARTStdioConfig(uint32_t ui32PortNum, uint32_t ui32Baud, uint32_t ui32SrcClock)

{

//

// Check the arguments.

//

ASSERT((ui32PortNum == 0) || (ui32PortNum == 1) ||

(ui32PortNum == 2));

 

#ifdef UART_BUFFERED

//

// In buffered mode, we only allow a single instance to be opened.

//

ASSERT(g_ui32Base == 0);

#endif

 

//

// Check to make sure the UART peripheral is present.

//

if(!MAP_SysCtlPeripheralPresent(g_ui32UARTPeriph[ui32PortNum]))

{

return;

}

 

//

// Select the base address of the UART.

//

g_ui32Base = g_ui32UARTBase[ui32PortNum];

 

//

// Enable the UART peripheral for use.

//

MAP_SysCtlPeripheralEnable(g_ui32UARTPeriph[ui32PortNum]);

 

//

// Configure the UART for 115200, n, 8, 1

//

MAP_UARTConfigSetExpClk(g_ui32Base, ui32SrcClock, ui32Baud,

(UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE |

UART_CONFIG_WLEN_8));

 

#ifdef UART_BUFFERED

//

// Set the UART to interrupt whenever the TX FIFO is almost empty or

// when any character is received.

//

MAP_UARTFIFOLevelSet(g_ui32Base, UART_FIFO_TX1_8, UART_FIFO_RX1_8);

 

//

// Flush both the buffers.

//

UARTFlushRx();

UARTFlushTx(true);

 

//

// Remember which interrupt we are dealing with.

//

g_ui32PortNum = ui32PortNum;

 

//

// We are configured for buffered output so enable the master interrupt

// for this UART and the receive interrupts.  We don't actually enable the

// transmit interrupt in the UART itself until some data has been placed

// in the transmit buffer.

//

MAP_UARTIntDisable(g_ui32Base, 0xFFFFFFFF);

MAP_UARTIntEnable(g_ui32Base, UART_INT_RX | UART_INT_RT);

MAP_IntEnable(g_ui32UARTInt[ui32PortNum]);

#endif

 

//

// Enable the UART operation.

//

MAP_UARTEnable(g_ui32Base);

}

如上可以看出,第三个参数的定义为:ui32SrcClock is the frequency of the source clock for the UART    module.所以在调用的时候指定为16000000.具体在使用中,可以直接使用上面的串口初始化示例,或进行修改。


UARTCharsAvail()


该程序的作用是读取串口的缓冲区中是否有数据,但是大多数时候被用来检测数据是否接收完成。当返回值为真时接收到数据,可以进一步处理数据。示例:

while(UARTCharsAvail(UART0_BASE))

    {

      //接收到数据后要进行的操作

      }

在谈及串口就不得不提FIFO的作用和价值,FIFO的作用在于减少中断的次数,避免CPU频繁进入串口中断。当来数据之后,会自动储存到FIFO中缓存,直到接收到行末或超过FIFO缓存,才会触发中断。

例如不使用FIFO接收字符串abc\n\r,则每接收一位则产生一次中断,但是数据还没有接收完成,所以需要继续等待直到接收完成。而当采用了FIFO后,可能就只会在接收完成数据之后再产生中断,提高了程序效率。可以通过UARTFIFOLevelSet()和UARTSpaceAvail()函数配置FIFO深度和获取FIFO空间状态。

 


UARTCharGet()    


UARTCharGet()函数用于从FIFO读取一位字符,并且会一直等到接收到数据为止。

 


UARTCharGetNonBlocking()    


UARTCharGetNonBlocking()    函数和UARTCharGet()函数最大的区别为前者不会等待,如果FIFO有数据未接收则接收,否则直接返回空。

 


UARTCharPut()


UARTCharPut()函数会将待发送的数据放到FIFO中,如果FIFO已经满了,则会等待至能够写入之后才会退出。示例:

UARTCharPut (UART0_BASE, 0x88);


UARTCharPutNonBlocking ()


本函数也用于向串口发送数据,但是不会等待FIFO出现空闲空间,无论FIFO是否已满都会发送数据,如果没有提前判断FIFO状态,可能会发生数据丢失。示例:

UARTCharPutNonBlocking (UART0_BASE, 0x88);


小结


简单的说,NonBlocking是将对串口直接操控的权限交给程序,例如在发送数据之前,需要程序判断FIFO是否还有空间,再调用NonBlocking类的发送函数。我们都知道,TI-TM4C123x是单核的处理器,倘若判断FIFO有空间之后即发送数据,不会产生数据的丢失或者意外的错误。

但是,倘若使用多个串口,并且在中断中自行设计了多级嵌套,可能还是会发生异常,不过这不是本文关注的问题了。


串口使用示例


#include <stdint.h>

#include <stdbool.h>

#include <string.h>

#include "inc/hw_ints.h"

#include "inc/hw_memmap.h"

#include "driverlib/fpu.h"

#include "driverlib/gpio.h"

#include "driverlib/interrupt.h"

#include "driverlib/pin_map.h"

#include "driverlib/rom.h"

#include "driverlib/rom_map.h"

#include "driverlib/sysctl.h"

#include "driverlib/uart.h"

 

void UARTIntHandler(void)

{

uint32_t ui32Status;

ui32Status = MAP_UARTIntStatus(UART0_BASE, true);

MAP_UARTIntClear(UART0_BASE, ui32Status);//清除标志位

while(MAP_UARTCharsAvail(UART0_BASE))//等待接收完成

{

MAP_UARTCharPutNonBlocking(UART0_BASE,MAP_UARTCharGetNonBlocking(UART0_BASE));//读取接收到的数据,并发回串口

                //当接收到数据后点亮LED

GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);

SysCtlDelay(SysCtlClockGet() / (1000 * 3));

GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);

                //注意,LED仅供测试用,如需用于生产用途,请注释这三行内容!!!

}

}

 

void    UARTSend(const uint8_t *pui8Buffer, uint32_t ui32Count)

{

while(ui32Count--)

{

MAP_UARTCharPutNonBlocking(UART0_BASE, *pui8Buffer++);

}

}

 

int    main(void)

{

MAP_FPUEnable();

MAP_FPULazyStackingEnable();

MAP_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |

SYSCTL_XTAL_16MHZ);

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

MAP_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

        //下面开始配置串口

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

MAP_IntMasterEnable();// Enable processor interrupts.

GPIOPinConfigure(GPIO_PA0_U0RX);

GPIOPinConfigure(GPIO_PA1_U0TX);

MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

MAP_UARTConfigSetExpClk(UART0_BASE, MAP_SysCtlClockGet(), 115200,

(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |

UART_CONFIG_PAR_NONE));

MAP_IntEnable(INT_UART0);// Enable the UART interrupt.

MAP_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);

        UARTIntRegister(UART0_BASE,UARTIntHandler);//注册中断服务程序句柄

UARTSend((uint8_t *)"\033[2JEnter text: ", 16);

while(1)

{

            ;

}

}

 


IIC



SPI



IO中断



定时器



ADC



代码结构



include头文件


#include <stdint.h>

stdint.hstdio.h最大的区别就是stdint使用具体的长度变量声明函数,在51STM32C编程中,使用类似short char int long或者unsigned char等关键词声明变量,但是为了解决在不同设备中不同的字长问题带来的移植问题,该头文件统一使用如int16_t或者uint16_t等关键词表示声明16位的整型变量和无符号型的16位整型变量。

 

#include <stdbool.h>

作者们都知道,在stdio.h中的bool实际占用的是一个int的空间,而在stdbool.h中的_Bool占用了bit空间。习惯在单片机编程中使用_Bool替换bool是一个好习惯。你可以通过如下代码查看对应的空间大小:

#include <stdio.h>

#include <stdbool.h>

int main(void)

{

bool a = true, b = false;

printf("a = %d,  b = %d\n", a, b);

printf("sizeof(_Bool) = %d\n", sizeof(_Bool));

}

 

#include "inc/hw_memmap.h"

类似51单片机的#include "reg51.h", hw_memmap.h引用了外设的映射地址,例如:

#define FLASH_BASE              0x00000000  // FLASH memory

#define SRAM_BASE               0x20000000  // SRAM memory

#define WATCHDOG0_BASE          0x40000000  // Watchdog0

#define WATCHDOG1_BASE          0x40001000  // Watchdog1

#define GPIO_PORTA_BASE         0x40004000  // GPIO Port A

#define GPIO_PORTB_BASE         0x40005000  // GPIO Port B

#define GPIO_PORTC_BASE         0x40006000  // GPIO Port C

#define GPIO_PORTD_BASE         0x40007000  // GPIO Port D

 

#include "inc/hw_types.h"

文件内提供了三种访问硬件寄存器的方式,分别为

HWREG(x)    ->    以32位方式访问寄存器

HWREGH(x)  ->    以16位方式访问寄存器

HWREGB(x)  ->    以8位方式访问寄存器

其中的x为硬件寄存器的地址。

为了提供位读写的效率,TI-TM4C123x基于的CortexM4内核采用了Bit-Banding技术,能够加快对于片内SRAM的操作速度。相应的函数有:

HWREGBITW(x,b)和HWREGBITH(x,b)和HWREGBITB(x,b)这三个函数用于访问寄存器x中的b位,不同在访问的不同字长的寄存器,具体可以参考官方的英文手册相关内容。

 

#include "driverlib/gpio.h"

Gpio.h中定义了端口的地址,还声明了对位操作的函数,具体参考上面GPIO章节的内容。


main()函数


如果你随便打开一个例程文件,你会发现main()函数的结构大多为:

int    main(void)

{

//-----------------其他内容1-----------------

MAP_FPUEnable();

MAP_FPULazyStackingEnable();

//-----------------其他内容2-----------------

    while(1)

    {

        //-----------------其他内容3-----------------

}

}

其他内容1主要是例如uint8_t x=2;等变量声明语句。

MAP_FPUEnable();

MAP_FPULazyStackingEnable();

这两句代码的作用为开启FPU,它可以加速浮点数的运算,什么是浮点数?可以简单理解为小数的运算。

接下来的其他内容2主要是各类功能或变量的初始化,例如:

MAP_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |

SYSCTL_XTAL_16MHZ);//配置系统时钟

其他内容3是在其他内容2执行完成后一直循环执行的内容,只要单片机没有发生复位,它不会停止。


参考例程



MPU9250


void MPU9050_Read_Data(vector3f *gyro,vector3f *accel,float *temperature)

{

    uint8_t buf[14];

    int16_t temp;

    i2cReadData(MPU_ADDR,ACCEL_XOUT_H,buf,14);

    accel->x=(int16_t)((buf[0]<<8)|buf[1]);

    accel->y=-(int16_t)((buf[2]<<8)|buf[3]);

    accel->z=-(int16_t)((buf[4]<<8)|buf[5]);

    temp=(int16_t)((buf[6]<<8)|buf[7]);

    gyro->x    =(int16_t)((buf[8]<<8)|buf[9]);

    gyro->y    =-(int16_t)((buf[10]<<8)|buf[11]);

    gyro->z    =-(int16_t)((buf[12]<<8)|buf[13]);

    *temperature=36.53f+(double)(temp/340.0f);

}

调用方式:

    MPU9250_Read_Data( &wgyro , &aaccel , &tempr );

    float Ax=aaccel.x/LSB_ACCEL;

    float Ay=aaccel.y/LSB_ACCEL;

    float Az=aaccel.z/LSB_ACCEL;

    float Angel_accX=atan(Ax/sqrt(Az*Az+Ay*Ay))*180/3.14;        //加速度计数据处理

float Angel_accY=atan(Ay/sqrt(Ax*Ax+Az*Az))*180/3.14;

float Angel_accZ=atan(Az/sqrt(Ax*Ax+Ay*Ay))*180/3.14;

    float gx=wgyro.x/LSB_GYRO;

    float gy=wgyro.y/LSB_GYRO;

    float gz=wgyro.z/LSB_GYRO;

    lasttime=nowtime;

    nowtime=millis();

    Gx=Gx+(gx-GxREG+g_Gyro_xoffset)*(nowtime-lasttime)/1000;    //陀螺仪数据处理

Gy=Gy+(gy-GyREG+g_Gyro_yoffset)*(nowtime-lasttime)/1000;

Gz=Gz+(gz-GzREG+g_Gyro_zoffset)*(nowtime-lasttime)/1000;

    GxREG=Gx,GyREG=Gy,GzREG=Gz;

    Gx=gx;

    XAxisREG+=Angel_accX;

    YAxisREG+=Angel_accY;

    ZAxisREG+=Angel_accZ;

    pitch=yijiehubu(Angel_accX,Gy,nowtime-lasttime);//一阶互补滤波函数整合数据

    roll=yijiehubu(Angel_accY,Gx,nowtime-lasttime);

    yaw=Angel_accZ;

    printf("PITCH:%f ROLL%f YAW:%f\n",pitch,roll,yaw);            //姿态角输出


OLED(SSD1306)


暂无


MOTOR(SG90S)


暂无


DAC(Based on R2R)


暂无


ADC(3PA1030)


暂无

« 上一篇 下一篇 »

返回顶部
请先 登录 再评论,若不是会员请先 注册