本文主要是为了备赛第十八届全国大学生智能车竞赛,基于逐飞开源库和芯片数据手册的一些学习总结,使用英飞凌官方的aurix development studio开发环境。
正如stm32的开发方式有标准库和hal库,infineon单片机也有官方库,而逐飞开源库则是在官方库的基础上又封装了一层,变成了易使用、易理解的api,极大的方便我们调用和编写程序。
那么对于智能车比赛,必须熟练掌握的内容有:gpio、定时器、(外部中断)、中断函数入口和优先级、串口的发送与接收、如何产生pwm、adc、如何读取正交编码器
目录
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主要有:
具体可以参考这篇文章:【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);开启定时器
所以我们不需要自己手动开启,但是要关闭的话得手动关闭
如果是简单的延时可以调用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函数
或者使用如下函数:
接收数据是比较难且重要的一部分,因为使用上位机调试或者蓝牙和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的占空比,就可以形成呼吸灯的效果
笔者水平有限,本文仅作总结与分享,欢迎批评指正,手动笔芯~
发表评论