本文主要是为了备赛第十八届全国大学生智能车竞赛,基于逐飞开源库和芯片数据手册的一些学习总结,使用英飞凌官方的AURIX Development Studio开发环境。

正如STM32的开发方式有标准库和HAL库,Infineon单片机也有官方库,而逐飞开源库则是在官方库的基础上又封装了一层,变成了易使用、易理解的API,极大的方便我们调用和编写程序。

那么对于智能车比赛,必须熟练掌握的内容有:GPIO、定时器、(外部中断)、中断函数入口和优先级、串口的发送与接收、如何产生PWM、ADC、如何读取正交编码器

目录

GPIO使用——点灯与按键检测

PIT产生简单的定时中断

中断函数入口和优先级

串口的收发——接收数据并解析(上位机调试)

PWM的产生——以呼吸灯为例

GPIO使用——点灯与按键检测

由芯片参考手册可知,TC264的GPIO端口号并不是连续的,每个端口号的引脚数目也不相同。但是在逐飞库中每个端口号都定义了16个引脚。

特别提醒:在设置IO时请自行根据硬件确认当前芯片是否具有此IO

需要注意仅P20_2是不能用于输出的,仅仅只有输入的功能

TC264DA芯片的21.6无法正常使用。这点是逐飞开源库中标注的,但是芯片手册中并未指出,暂时不知道具体原因。

下面介绍几个常用的API函数:

引脚初始化:void gpio_init (gpio_pin_enum pin, gpio_dir_enum dir, uint8 dat, gpio_mode_enum pinmode)设置引脚输出电平:void gpio_set_level (gpio_pin_enum pin, uint8 dat)获取引脚输入电平:uint8 gpio_get_level (gpio_pin_enum pin)翻转引脚电平:void gpio_toggle_level (gpio_pin_enum pin)

在初始化引脚的函数中,对引脚电平的设置只在引脚设置为输出模式(GPO)下才有用

pinmode主要有:

typedef enum {     GPI_FLOATING_IN,       // 定义管脚浮空输入     GPI_PULL_UP    ,       // 定义管脚上拉输入     GPI_PULL_DOWN  ,       // 定义管脚下拉输入

    GPO_PUSH_PULL  ,       // 定义管脚推挽输出     GPO_OPEN_DTAIN ,       // 定义管脚开漏输出 }gpio_mode_enum;

具体可以参考这篇文章:【STM32】STM32F4 GPIO八种模式及工作原理详解

PIT产生简单的定时中断

常用API函数有:

初始化PIT定时器:void pit_init (pit_index_enum pit_index, uint32 time)对pit_init的封装,方便设置ms级定时器中断:pit_ms_init(pit_index, time)对pit_init的封装,方便设置us级定时器中断:pit_us_init(pit_index, time)

Infineon单片机中并没有PIT外设,这些函数是对官方库中CCU6模块的封装

在初始化函数中由这句restoreInterrupts(interrupt_state);开启中断

由这句IfxCcu6_Timer_start(&g_Ccu6Timer);开启定时器

所以我们不需要自己手动开启,但是要关闭的话得手动关闭

void pit_close               (pit_index_enum pit_index); void pit_start               (pit_index_enum pit_index); void pit_all_close           (void); void pit_disable             (pit_index_enum pit_index); void pit_enable              (pit_index_enum pit_index);

如果是简单的延时可以调用systick函数 

中断函数入口和优先级

逐飞的工程目录如下:

  

其中code文件夹是我们存放自己编写的.c、.h文件,libraries是库文件,不需要改动,user文件夹下isr.c中是所有的中断处理函数入口,可以直接类比到STM32的IRQHandler函数 

 并且逐飞已经添加了去除中断标志的函数,我们使用时可以直接把这部分复制到main.c中然后进行后续开发。

 isr_config.h文件下是所有中断的优先级定义和决定中断由谁处理

TC264是双核的芯片,但是如何在不出错的情况下合理使用两个CPU笔者还未研究,目前只使用CPU0

特别注意:所有中断优先级都必须设置为不一样的值,TC264具有255个中断优先级可以设置

1-255,0优先级表示不开启中断,255为最高优先级

 下面举一个利用状态机进行按键检测的例子,主要涉及GPIO的基础API、PIT的20ms定时中断。这样完成的按键检测,既实现了消抖,也不会让CPU空等。如果利用外部中断进行按键检测,如果按键长按,可能多次进入中断,或者必须在中断函数中等待按键松开。

typedef enum

{

KEY_CHECK = 0, //按键检测状态

KEY_COMFIRM, //按键确认状态

KEY_UNPRESSED //按键释放状态

}keyState_e; //状态枚举变量

typedef struct

{

keyState_e keyState; //按键状态

uint8 keyFlag; //按键按下标志

}key_t; //按键状态结构体

key_t Key[4];

void KeyCheck(int i, key_t *Key, gpio_pin_enum pin)

{

switch( Key[i].keyState )

{

case KEY_CHECK:

// 读到低电平,进入按键确认状态

if(gpio_get_level(pin) == GPIO_LOW)

{

Key[i].keyState = KEY_COMFIRM;

}

break;

case KEY_COMFIRM:

if(gpio_get_level(pin) == GPIO_LOW)

{

//读到低电平,按键确实按下,按键标志位置1,并进入按键释放状态

Key[i].keyState = KEY_UNPRESSED;

Key[i].keyFlag = 1;

}

//读到高电平,可能是干扰信号,返回初始状态

else

{

Key[i].keyState = KEY_CHECK;

}

break;

case KEY_UNPRESSED:

if(gpio_get_level(pin) == GPIO_HIGH)

{

// 读到高电平,说明按键释放,返回初始状态

Key[i].keyState = KEY_CHECK;

}

break;

default: break;

}

}

int core0_main(void)

{

//定义变量

uint8 i;

clock_init(); // 获取时钟频率<务必保留>

debug_init(); // 初始化默认调试串口

// 此处编写用户代码 例如外设初始化代码等

//由于逐飞的母版上按键电路有电阻上拉到高电平,所有GPIO设置为浮空输入即可

gpio_init(BUTTON1, GPI, GPIO_LOW, GPI_FLOATING_IN);

gpio_init(BUTTON2, GPI, GPIO_LOW, GPI_FLOATING_IN);

gpio_init(BUTTON3, GPI, GPIO_LOW, GPI_FLOATING_IN);

gpio_init(BUTTON4, GPI, GPIO_LOW, GPI_FLOATING_IN);

pit_ms_init(PIT_NUM, 20); // 初始化 CCU6_0_CH0 为周期中断

// 此处编写用户代码 例如外设初始化代码等

cpu_wait_event_ready(); // 等待所有核心初始化完毕

while (TRUE)

{

// 此处编写需要循环执行的代码

if(pit_state)

{

KeyCheck(0,Key,BUTTON1);

KeyCheck(1,Key,BUTTON2);

KeyCheck(2,Key,BUTTON3);

KeyCheck(3,Key,BUTTON4);

pit_state = 0; // 清空周期中断触发标志位

}

for(i = 0;i<4;i++)

{

if(Key[i].keyFlag == 1)

{

printf("button %d pressed!\n",i);

Key[i].keyFlag = 0; //清除按键按下标志

}

}

}

}

IFX_INTERRUPT(cc60_pit_ch0_isr, 0, CCU6_0_CH0_ISR_PRIORITY)

{

interrupt_global_enable(0); // 开启中断嵌套

pit_clear_flag(CCU60_CH0);

pit_state = 1;

}

串口的收发——接收数据并解析(上位机调试)

在debug_init函数中逐飞已经完成了默认串口的初始化,默认的串口是

 串口发送数据很简单,逐飞库已经完成了printf函数的重定向,可以直接调用printf函数

或者使用如下函数:

void    uart_write_byte_wait                (uart_index_enum uart_n, const uint8 dat); void    uart_write_byte                     (uart_index_enum uartn, const uint8 dat); void    uart_write_buffer                   (uart_index_enum uartn, const uint8 *buff, uint32 len); void    uart_write_string                   (uart_index_enum uartn, const char *str);

接收数据是比较难且重要的一部分,因为使用上位机调试或者蓝牙和wifi等都需要串口能正确接收我们发送的数据并解析

本文仅讨论接收定长数据并解析,因为我们调试的过程中一般会按固定的格式发送

主要思路为:开启串口接收中断,把读到的数据先存入fifo,当读到指定数目后进入checkCmd函数检查格式,如果格式正确就进入usart_analysis

注意处理完毕要及时清空接受数组在进行下一次接收

如果有更好的思路欢迎分享

#define UART_INDEX (DEBUG_UART_INDEX ) // 默认 UART_0

#define UART_BAUDRATE (DEBUG_UART_BAUDRATE) // 默认 115200

#define UART_TX_PIN (DEBUG_UART_TX_PIN ) // 默认 UART0_TX_P14_0

#define UART_RX_PIN (DEBUG_UART_RX_PIN ) // 默认 UART0_RX_P14_1

#define LED1 P20_9

uint8 uart_get_data[64]; // 串口接收数据缓冲区

uint8 fifo_get_data[64]; // fifo 输出读出缓冲区

uint8 get_data = 0; // 接收数据变量

uint32 fifo_data_count = 0; // fifo 数据个数

fifo_struct uart_data_fifo;

int core0_main(void)

{

clock_init(); // 获取时钟频率<务必保留>

debug_init(); // 初始化默认调试串口

// 此处编写用户代码 例如外设初始化代码等

fifo_init(&uart_data_fifo, FIFO_DATA_8BIT, uart_get_data, 64); // 初始化 fifo 挂载缓冲区

gpio_init(LED1, GPO, GPIO_HIGH, GPO_OPEN_DTAIN);

uart_rx_interrupt(UART_INDEX, 1); // 开启 UART_INDEX 的接收中断

cpu_wait_event_ready(); // 等待所有核心初始化完毕

while (TRUE)

{

// 此处编写需要循环执行的代码

}

}

_Bool checkCmd(uint8_t* str)

{

//仅作举例 CNBR:A392:200202120000 蓝桥杯误入

if((str[0] == 'C' || str[0] == 'V') && str[1] == 'N' && str[2] == 'B' && str[3] == 'R' && str[4] == ':' && str[9] == ':')

{

uint8_t i;

for(i = 10; i < 22; i++)

{

if(str[i] > '9' || str[i] < '0')

return 0;

}

return 1;

}

else

{

memset(fifo_get_data,0,22);

return 0;

}

}

//-------------------------------------------------------------------------------------------------------------------

// 函数简介 UART_INDEX 的接收中断处理函数 这个函数将在 UART_INDEX 对应的中断调用

// 参数说明 void

// 返回参数 void

// 使用示例 uart_rx_interrupt_handler();

//-------------------------------------------------------------------------------------------------------------------

void uart_rx_interrupt_handler (void)

{

// get_data = uart_read_byte(UART_INDEX); // 接收数据 while 等待式 不建议在中断使用

uart_query_byte(UART_INDEX, &get_data); // 接收数据 查询式 有数据会返回 TRUE 没有数据会返回 FALSE

fifo_write_buffer(&uart_data_fifo, &get_data, 1); // 将数据写入 fifo 中

fifo_data_count = fifo_used(&uart_data_fifo); // 查询数组当前数据个数

if(fifo_data_count == 22) //读到指定数目后开始解析

{

fifo_read_buffer(&uart_data_fifo, fifo_get_data, &fifo_data_count, FIFO_READ_AND_CLEAN); // 将 fifo 中数据读出并清空 fifo 挂载的缓冲

//一些格式判断语句,防止有数据丢失导致格式错误

if(checkCmd(fifo_get_data))

{

gpio_toggle_level(LED1);

//usart_analysis(); 串口解析函数,需要自己编写

memset(fifo_get_data,0,fifo_data_count);

}

}

}

IFX_INTERRUPT(uart0_rx_isr, 0, UART0_RX_INT_PRIO)

{

interrupt_global_enable(0); // 开启中断嵌套

IfxAsclin_Asc_isrReceive(&uart0_handle);

uart_rx_interrupt_handler(); // 串口接收处理

}

PWM的产生——以呼吸灯为例

常用API函数有: 

PWM初始化:void pwm_init (pwm_channel_enum pwmch, uint32 freq, uint32 duty)设置占空比:void pwm_set_duty (pwm_channel_enum pwmch, uint32 duty)

在初始化函数中已经由IfxGtm_Atom_Pwm_start(&g_atomDriver, TRUE);打开了PWM,不需要手动开启

可产生PWM的引脚不是任意的,只能选用逐飞定义的枚举变量

最大占空比PWM_DUTY_MAX默认为10000,可在头文件中自行修改

for(duty = 0; duty <= PWM_DUTY_MAX / 2; duty ++) // 输出占空比递增到 50%

{

// 呼吸流水灯

for(channel_index = 0; channel_index < CHANNEL_NUMBER; channel_index++)

{

duty_temp = (duty + channel_index * PWM_DUTY_MAX / 8) % (PWM_DUTY_MAX / 2) + (PWM_DUTY_MAX / 2);

pwm_set_duty(channel_list[channel_index], duty_temp); // 更新对应通道占空比

}

system_delay_us(200);

}

每隔一段时间改变一次PWM的占空比,就可以形成呼吸灯的效果

笔者水平有限,本文仅作总结与分享,欢迎批评指正,手动笔芯~

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: