概述
ADC环境例程与库函数学习
ADC模块
ADC模块:即模数转换模块/AD转换模块,功能是将电压信号转换为相应的数字信号。实际应用中,这个电压信号可能由温度、湿度、压力等实际物理量经过传感器和相应的变换电路转化而来。经过AD转换,MCU(微处理器;单片机)就可以处理这些物理量。
//以下是两句常用语
#include "headfile.h"
#pragma section all "cpu0_dsram"//这两句为常用
uint16 adc_result;
uint16来源
/* 16bit unsigned: 0..65535 [0x0000..0xFFFF]*/
/* [cover parentID={66E964CA-35D5-4013-BB61-1E824636D713}] uint16 [/cover] */
typedef unsigned short uint16;
主函数
int core0_main(void)
{
disableInterrupts();//禁用中断
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
adc_init(ADC_0, ADC0_CH0_A0);//初始化ADC0 通道0 使用A0引脚
enableInterrupts();//启动中断
while (TRUE)
{
//通过将A0引脚接到GND或者核心板VCC 查看FSS窗口中打印出来的ADC结果
//A0接到GND时 结果应该在0附近
//A0接到VCC时 结果应该在4095附近
//悬空时 结果可能是任意数,没有任何意义,所以大家不要问悬空的时候为什么结果是这么多呢?
adc_result = adc_mean_filter(ADC_0, ADC0_CH0_A0, ADC_12BIT, 10);//采集10次求平均 分辨率12位
//将结果通过串口打印,可以先学习printf例程,了解如何使用printf
printf("adc_result: %dn", adc_result);
systick_delay_ms(STM0, 100);
}
}
//以下又是一波客套话
#pragma section all restore
systick_delay_ms意义
//------------------------------------以下宏定义用于延时------------------------------------
#define systick_delay_ms(stmn, time) systick_delay(stmn, time*1000000) //设置延时时间 单位ms
ADC均值滤波
//-------------------------------------------------------------------------------------------------------------------
// @brief ADC均值滤波
// @param adcn 选择ADC模块(ADC_0、ADC_1、ADC_2)
// @param ch 选择ADC通道
// @param count 均值滤波次数
// @return void
// Sample usage: adc_mean_filter(ADC_0, ADC0_CH0_A0, ADC_12BIT, 5);//采集5次 然后返回平均值
//-------------------------------------------------------------------------------------------------------------------
uint16 adc_mean_filter(VADCN_enum vadc_n, VADC_CHN_enum vadc_chn, VADC_RES_enum vadc_res, uint8 count)
{
ZF_ASSERT(count);//断言次数不能为0
sum = 0;
for(i=0; i<count; i++)
{
sum += adc_convert(vadc_n, vadc_chn, vadc_res);
}
sum = sum/count;
return (uint16)sum;
}
ADC转换
//-------------------------------------------------------------------------------------------------------------------
// @brief ADC转换一次即获取一次信息
// @param adcn 选择ADC模块(ADC_0、ADC_1、ADC_2)
// @param ch 选择ADC通道
// @return void
// Sample usage: adc_convert(ADC_0, ADC0_CH0_A0, ADC_12BIT);
//-------------------------------------------------------------------------------------------------------------------
uint16 adc_convert(VADCN_enum vadc_n, VADC_CHN_enum vadc_chn, VADC_RES_enum vadc_res)
{
Ifx_VADC_RES result;
uint8 temp;
do
{
result = IfxVadc_getResult(&MODULE_VADC.G[vadc_n], vadc_chn%16);
} while (!result.B.VF);
temp = 4 - (vadc_res * 2);
return((result.U&0x0fff)>>temp);
}
LED闪烁模块
//老客套话了
#include "headfile.h"
#pragma section all "cpu0_dsram"
uint8 gpio_status;
//GPIO(General Purpose I/O Ports)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平。
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
//需要特别注意P20_2是不能用于输出的,仅仅只有输入的功能
//需要特别注意P20_2是不能用于输出的,仅仅只有输入的功能
//需要特别注意P20_2是不能用于输出的,仅仅只有输入的功能
//设置P20_8为输出 默认输出低电平 PUSHPULL:推挽输出
gpio_init(P20_8, GPO, 0, PUSHPULL);
gpio_init(P20_9, GPO, 0, PUSHPULL);
gpio_init(P21_4, GPO, 0, PUSHPULL);
gpio_init(P21_5, GPO, 0, PUSHPULL);
//设置P21_2为输入 PULLUP:上拉输入
gpio_init(P21_2, GPI, 0, PULLUP);
enableInterrupts();
while (TRUE)
{
//获取gpio状态
gpio_status = gpio_get(P21_2);
//将gpio状态打印到FSS窗口
printf("gpio_status: %dn", gpio_status);
gpio_toggle(P20_8);//翻转IO
gpio_toggle(P20_9);
gpio_toggle(P21_4);
gpio_toggle(P21_5);
systick_delay_ms(STM0, 100);
}
}
#pragma section all restore
枚举上下拉选项
// 枚举上下拉选项
typedef enum
{
NO_PULL, //无输入上下拉
PULLUP, //输入上拉
PULLDOWN, //输入下拉
PUSHPULL, //推挽输出
OPENDRAIN, //开漏输出
}GPIOMODE_enum;
GPIO状态获取
//-------------------------------------------------------------------------------------------------------------------
// @brief GPIO状态获取
// @param pin 选择的引脚 (可选择范围由 common.h 内PIN_enum枚举值确定)
// @return uint8 0:低电平 1:高电平
// Sample usage: uint8 status = gpio_get(P00_0);//获取P00_0引脚电平
//-------------------------------------------------------------------------------------------------------------------
uint8 gpio_get(PIN_enum pin)
{
return IfxPort_getPinState(get_port(pin), pin&0x1f);
}
GPIO 翻转
//-------------------------------------------------------------------------------------------------------------------
// @brief GPIO 翻转
// @param pin 选择的引脚 (可选择范围由 common.h 内PIN_enum枚举值确定)
// @return void
// Sample usage: gpio_toggle(P00_0);//P00_0引脚电平翻转
//-------------------------------------------------------------------------------------------------------------------
void gpio_toggle(PIN_enum pin)
{
IfxPort_togglePin(get_port(pin), pin&0x1f);
}
周期定时器中断
#include "headfile.h"
#pragma section all "cpu0_dsram"
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//在此处调用各种初始化函数等
//使用CCU6_0模块的通道0 产生一个 100ms的周期中断
pit_interrupt_ms(CCU6_0, PIT_CH0, 100);
//中断函数在isr.c中 函数名称为cc60_pit_ch0_isr
//中断相关的配置参数在isr_config.h内
//可配置参数有 CCU6_0_CH0_INT_SERVICE 和 CCU6_0_CH0_ISR_PRIORITY
//CCU6_0_CH0_INT_SERVICE 中断服务者,表示改中断由谁处理,0:CPU0 1:CPU1 3:DMA 不可设置为其他值
//CCU6_0_CH0_ISR_PRIORITY 中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//需要特备注意的是 不可以有优先级相同的中断函数 每个中断的优先级都必须是不一样的
enableInterrupts();
while (TRUE)
{
//在isr.c的中断函数,函数定义的第二个参数固定为0,请不要更改,即使你用CPU1处理中断也不要更改,需要CPU1处理中断只需要在isr_config.h内修改对应的宏定义即可
//程序运行之后 PIT中断每执行一次就会打印一次
//将结果通过串口打印,可以先学习printf例程,了解如何使用printf
}
}
#pragma section all restore
捕获/比较单元6(CCU6模块)
CCU是一个具有特定应用模式的16位高分辨率捕获和比较单元,主要用于AC驱动控制。特殊操作模式支持使用霍尔传感器或反电动势检测的无刷DC电机。
CCU6单元由一个有三个捕获/比较通道的定时器T12块和一个有一个捕获比较通道的定时器T13定时器块组成。T12通道可以独立产生PWM信号、接受捕获的触发信号或他们联合产生控制信号模式来驱动AC电机或变频器。
注:捕获/比较模块被命名CCU6(捕获/比较单元6)。该模块内部的捕获/比较通道被命名为CC6x。
PIT中断
//------------------------------------以下代码用于PIT中断------------------------------------
#define pit_interrupt_ms(ccu6n, pit_ch, time) pit_init(ccu6n, pit_ch, time*1000) //(单位为 毫秒)
#define pit_interrupt_us(ccu6n, pit_ch, time) pit_init(ccu6n, pit_ch, time) //(单位为 微秒)
PIT中断参数相关定义
//------------PIT中断参数相关定义------------
#define CCU6_0_CH0_INT_SERVICE 0 //定义CCU6_0 PIT通道0中断服务类型,即中断是由谁响应处理 0:CPU0 1:CPU1 3:DMA 不可设置为其他值
#define CCU6_0_CH0_ISR_PRIORITY 30 //定义CCU6_0 PIT通道0中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
#define CCU6_0_CH1_INT_SERVICE 0
#define CCU6_0_CH1_ISR_PRIORITY 31
#define CCU6_1_CH0_INT_SERVICE 0
#define CCU6_1_CH0_ISR_PRIORITY 32
#define CCU6_1_CH1_INT_SERVICE 0
#define CCU6_1_CH1_ISR_PRIORITY 33
编码器
#include "headfile.h"
#pragma section all "cpu0_dsram"
int16 speed;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
//第一个参数 表示选择使用的定时器
//第二个参数 表示选择的计数引脚 计数引脚与方向引脚不可交换
//第三个参数 表示选择的方向引脚
gpt12_init(GPT12_T2, GPT12_T2INB_P33_7, GPT12_T2EUDB_P33_6);
enableInterrupts();
while (TRUE)
{
speed = gpt12_get(GPT12_T2);
gpt12_clear(GPT12_T2);
//打印采集到的编码器数据,可以先学习printf例程,了解如何使用printf
printf("speed: %dn", speed);
systick_delay_ms(STM0, 100);
//旋转编码器越快显示的数据越大,正转数值为正,反正数值为负
}
}
#pragma section all restore
GPT12初始化(编码器采集)
//-------------------------------------------------------------------------------------------------------------------
// @brief GPT12初始化(编码器采集)
// @param gptn 选择所使用的GPT12定时器
// @param count_pin 设置计数引脚
// @param dir_pin 设置计数方向引脚
// @return void
// Sample usage: gpt12_init(GPT12_T2, GPT12_T2INA_P00_7, GPT12_T2EUDA_P00_8);//使用T2定时器 P00_7引脚进行计数 计数方向使用P00_8引脚
//-------------------------------------------------------------------------------------------------------------------
void gpt12_init(GPTN_enum gptn, GPT_PIN_enum count_pin, GPT_PIN_enum dir_pin)
{
IfxGpt12_enableModule(&MODULE_GPT120);
IfxGpt12_setGpt1BlockPrescaler(&MODULE_GPT120, IfxGpt12_Gpt1BlockPrescaler_4);
IfxGpt12_setGpt2BlockPrescaler(&MODULE_GPT120, IfxGpt12_Gpt2BlockPrescaler_4);
gpt12_mux(gptn, count_pin, dir_pin);
switch(gptn)
{
case GPT12_T2:
{
IfxGpt12_T2_setCounterInputMode(&MODULE_GPT120, IfxGpt12_CounterInputMode_risingEdgeTxIN);
IfxGpt12_T2_setDirectionSource(&MODULE_GPT120, IfxGpt12_TimerDirectionSource_external);
IfxGpt12_T2_setMode(&MODULE_GPT120, IfxGpt12_Mode_counter);
IfxGpt12_T2_run(&MODULE_GPT120, IfxGpt12_TimerRun_start);
}break;
case GPT12_T3:
{
IfxGpt12_T3_setCounterInputMode(&MODULE_GPT120, IfxGpt12_CounterInputMode_risingEdgeTxIN);
IfxGpt12_T3_setDirectionSource(&MODULE_GPT120, IfxGpt12_TimerDirectionSource_external);
IfxGpt12_T3_setMode(&MODULE_GPT120, IfxGpt12_Mode_counter);
IfxGpt12_T3_run(&MODULE_GPT120, IfxGpt12_TimerRun_start);
}break;
case GPT12_T4:
{
IfxGpt12_T4_setCounterInputMode(&MODULE_GPT120, IfxGpt12_CounterInputMode_risingEdgeTxIN);
IfxGpt12_T4_setDirectionSource(&MODULE_GPT120, IfxGpt12_TimerDirectionSource_external);
IfxGpt12_T4_setMode(&MODULE_GPT120, IfxGpt12_Mode_counter);
IfxGpt12_T4_run(&MODULE_GPT120, IfxGpt12_TimerRun_start);
}break;
case GPT12_T5:
{
IfxGpt12_T5_setCounterInputMode(&MODULE_GPT120, IfxGpt12_CounterInputMode_risingEdgeTxIN);
IfxGpt12_T5_setDirectionSource(&MODULE_GPT120, IfxGpt12_TimerDirectionSource_external);
IfxGpt12_T5_setMode(&MODULE_GPT120, IfxGpt12_Mode_counter);
IfxGpt12_T5_run(&MODULE_GPT120, IfxGpt12_TimerRun_start);
}break;
case GPT12_T6:
{
IfxGpt12_T6_setCounterInputMode(&MODULE_GPT120, IfxGpt12_CounterInputMode_risingEdgeTxIN);
IfxGpt12_T6_setDirectionSource(&MODULE_GPT120, IfxGpt12_TimerDirectionSource_external);
IfxGpt12_T6_setMode(&MODULE_GPT120, IfxGpt12_Mode_counter);
IfxGpt12_T6_run(&MODULE_GPT120, IfxGpt12_TimerRun_start);
}break;
}
}
GPT12定时器编号
typedef enum // 枚举GPT12 定时器编号
{
GPT12_T2,
GPT12_T3,
GPT12_T4,
GPT12_T5,
GPT12_T6,
}GPTN_enum;
枚举GPT12引脚
//此枚举定义不允许用户修改
typedef enum //枚举GPT12引脚
{
GPT12_T2INA_P00_7, GPT12_T2INB_P33_7, //T2定时器 计数引脚可选范围
GPT12_T2EUDA_P00_8, GPT12_T2EUDB_P33_6, //T2定时器 计数方向引脚可选范围
//在LQFP144封装中没有P10_4 、P10_7
GPT12_T3INA_P02_6, GPT12_T3INB_P10_4, //T3定时器 计数引脚可选范围
GPT12_T3EUDA_P02_7, GPT12_T3EUDB_P10_7, //T3定时器 计数方向引脚可选范围
//在LQFP144封装中没有P10_8
GPT12_T4INA_P02_8, GPT12_T4INB_P10_8, //T4定时器 计数引脚可选范围
GPT12_T4EUDA_P00_9, GPT12_T4EUDB_P33_5, //T4定时器 计数方向引脚可选范围
GPT12_T5INB_P10_3, GPT12_T5INA_P21_7, //T5定时器 计数引脚可选范围
GPT12_T5EUDB_P10_1, GPT12_T5EUDA_P21_6, //T5定时器 计数方向引脚可选范围
//在LQFP144封装中没有P10_0
GPT12_T6INB_P10_2, GPT12_T6INA_P20_3, //T6定时器 计数引脚可选范围
GPT12_T6EUDB_P10_0, GPT12_T6EUDA_P20_0, //T6定时器 计数方向引脚可选范围
}GPT_PIN_enum;
GPT12计数读取(编码器采集)
//-------------------------------------------------------------------------------------------------------------------
// @brief GPT12计数读取(编码器采集)
// @param gptn 选择所使用的GPT12定时器
// @return void
// Sample usage: speed = gpt12_get(GPT12_T2);//使用T2定时器
//-------------------------------------------------------------------------------------------------------------------
int16 gpt12_get(GPTN_enum gptn)
{
switch(gptn)
{
case GPT12_T2: return (int16)IfxGpt12_T2_getTimerValue(&MODULE_GPT120);
case GPT12_T3: return (int16)IfxGpt12_T3_getTimerValue(&MODULE_GPT120);
case GPT12_T4: return (int16)IfxGpt12_T4_getTimerValue(&MODULE_GPT120);
case GPT12_T5: return (int16)IfxGpt12_T5_getTimerValue(&MODULE_GPT120);
case GPT12_T6: return (int16)IfxGpt12_T6_getTimerValue(&MODULE_GPT120);
default: return 0;
}
}
清除GPT12计数(编码器采集)
//-------------------------------------------------------------------------------------------------------------------
// @brief 清除GPT12计数(编码器采集)
// @param gptn 选择所使用的GPT12定时器
// @return void
// Sample usage: gpt12_clear(GPT12_T2);//使用T2定时器
//-------------------------------------------------------------------------------------------------------------------
void gpt12_clear(GPTN_enum gptn)
{
switch(gptn)
{
case GPT12_T2: IfxGpt12_T2_setTimerValue(&MODULE_GPT120, 0); break;
case GPT12_T3: IfxGpt12_T3_setTimerValue(&MODULE_GPT120, 0); break;
case GPT12_T4: IfxGpt12_T4_setTimerValue(&MODULE_GPT120, 0); break;
case GPT12_T5: IfxGpt12_T5_setTimerValue(&MODULE_GPT120, 0); break;
case GPT12_T6: IfxGpt12_T6_setTimerValue(&MODULE_GPT120, 0); break;
}
}
串口的发送与中断接受
#include "headfile.h"
#pragma section all "cpu0_dsram"
uint8 uart_buff;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
uart_init(UART_0, 115200, UART0_TX_P14_0, UART0_RX_P14_1);
//在本库中,对于串口的接收与发送都是通过中断完成的,因此想要正常的使用串口功能务必保证中断是开启的,也就是调用了enableInterrupts()
enableInterrupts();
//调用enableInterrupts函数之后 才开始发送或者接收数据
uart_putstr(UART_0, "n---uart test---n");
//串口的中断函数全部都在isr.c中。
//串口中断相关的配置参数都在isr_config.h中
//可配置参数有 ERU_CH0_CH4_INT_SERVICE 和 ERU_CH0_CH4_INT_PRIO
//UART0_INT_SERVICE 中断服务者,表示改中断由谁处理,0:CPU0 1:CPU1 3:DMA 不可设置为其他值
//UART0_TX_INT_PRIO 发送中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//UART0_RX_INT_PRIO 接收中断优先级
//UART0_ER_INT_PRIO 错误中断优先级
//其他串口中断参数同理
//需要特备注意的是 不可以有优先级相同的中断函数 每个中断的优先级都必须是不一样的
while (TRUE)
{
//查询是否有数据,如果有则接收
if(uart_query(UART_0, &uart_buff))
{
//将收到的数据,再发出去
uart_putchar(UART_0, uart_buff);
}
//使用usb转ttl连接单片机的串口,在电脑上打开串口助手,任意发送字符,
//单片机收到后会返回发送的字符,串口助手就会显示刚才发送的东西
}
}
#pragma section all restore
串口初始化
//-------------------------------------------------------------------------------------------------------------------
// @brief 串口初始化
// @param uartn 串口模块号(UART_0,UART_1,UART_2,UART_3)
// @param baud 串口波特率
// @param tx_pin 串口发送引脚
// @param rx_pin 串口接收引脚
// @return uint32 实际波特率
// Sample usage: uart_init(UART_0,115200,UART0_TX_P14_0,UART0_RX_P14_1); // 初始化串口0 波特率115200 发送引脚使用P14_0 接收引脚使用P14_1
//-------------------------------------------------------------------------------------------------------------------
void uart_init(UARTN_enum uartn, uint32 baud, UART_PIN_enum tx_pin, UART_PIN_enum rx_pin)
{
boolean interrupt_state = disableInterrupts();
volatile Ifx_ASCLIN *moudle = IfxAsclin_getAddress((IfxAsclin_Index)uartn);
IfxAsclin_Asc_initModuleConfig(&uart_config, moudle); //初始化化配置结构体
uart_set_buffer(uartn);//设置缓冲区
uart_set_interrupt_priority(uartn);//设置中断优先级
uart_config.baudrate.prescaler = 4;
uart_config.baudrate.baudrate = (float32)baud;
uart_config.baudrate.oversampling = IfxAsclin_OversamplingFactor_8;
IfxAsclin_Asc_Pins pins;//设置引脚
pins.cts = NULL;
pins.rts = NULL;
uart_mux(uartn, tx_pin, rx_pin, (uint32 *)&pins.tx, (uint32 *)&pins.rx);
pins.rxMode = IfxPort_InputMode_pullUp;
pins.txMode = IfxPort_OutputMode_pushPull;
pins.pinDriver = IfxPort_PadDriver_cmosAutomotiveSpeed1;
uart_config.pins = &pins;
IfxAsclin_Asc_initModule(uart_get_handle(uartn), &uart_config);
restoreInterrupts(interrupt_state);
}
GPIO中断参数相关定义
//------------GPIO中断参数相关定义------------
//通道0与通道4是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH0_CH4_INT_SERVICE 0 //定义ERU通道0和通道4中断服务类型,即中断是由谁响应处理 0:CPU0 1:CPU1 3:DMA 不可设置为其他值
#define ERU_CH0_CH4_INT_PRIO 40 //定义ERU通道0和通道4中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//通道1与通道5是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH1_CH5_INT_SERVICE 0 //定义ERU通道1和通道5中断服务类型,同上
#define ERU_CH1_CH5_INT_PRIO 41 //定义ERU通道1和通道5中断优先级 同上
//通道2与通道6是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH2_CH6_INT_SERVICE 3 //定义ERU通道2和通道6中断服务类型,同上
#define ERU_CH2_CH6_INT_PRIO 5 //定义ERU通道2和通道6中断优先级 同上
//通道3与通道7是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH3_CH7_INT_SERVICE 0 //定义ERU通道3和通道7中断服务类型,同上
#define ERU_CH3_CH7_INT_PRIO 43 //定义ERU通道3和通道7中断优先级 同上
读取串口接收的数据(查询接收)
//-------------------------------------------------------------------------------------------------------------------
// @brief 读取串口接收的数据(查询接收)
// @param uartn 串口模块号(UART_0,UART_1,UART_2,UART_3)
// @param *dat 接收数据的地址
// @return uint8 1:接收成功 0:未接收到数据
// Sample usage: uint8 dat; uart_query(UART_0,&dat); // 接收串口0数据 存在在dat变量里
//-------------------------------------------------------------------------------------------------------------------
uint8 uart_query(UARTN_enum uartn, uint8 *dat)
{
if(IfxAsclin_Asc_getReadCount(uart_get_handle(uartn)) >0)
{
*dat = IfxAsclin_Asc_blockingRead(uart_get_handle(uartn));
return 1;
}
return 0;
}
串口字节输出
//-------------------------------------------------------------------------------------------------------------------
// @brief 串口字节输出
// @param uartn 串口模块号(UART_0,UART_1,UART_2,UART_3)
// @param dat 需要发送的字节
// @return void
// Sample usage: uart_putchar(UART_0, 0xA5); // 串口0发送0xA5
//-------------------------------------------------------------------------------------------------------------------
void uart_putchar(UARTN_enum uartn, uint8 dat)
{
// IfxAsclin_Asc_blockingWrite(uart_get_handle(uartn),dat);
Ifx_SizeT count = 1;
(void)IfxAsclin_Asc_write(uart_get_handle(uartn), &dat, &count, TIME_INFINITE);
}
串口发送数组
//-------------------------------------------------------------------------------------------------------------------
// @brief 串口发送数组
// @param uartn 串口模块号(UART_0,UART_1,UART_2,UART_3)
// @param *buff 要发送的数组地址
// @param len 发送长度
// @return void
// Sample usage: uart_putbuff(UART_0,&a[0],5);
//-------------------------------------------------------------------------------------------------------------------
void uart_putbuff(UARTN_enum uartn, uint8 *buff, uint32 len)
{
while(len)
{
uart_putchar(uartn, *buff);
len--;
buff++;
}
}
串口发送字符串
//-------------------------------------------------------------------------------------------------------------------
// @brief 串口发送字符串
// @param uartn 串口模块号(UART_0,UART_1,UART_2,UART_3)
// @param *str 要发送的字符串地址
// @return void
// Sample usage: uart_putstr(UART_0,"i lvoe you");
//-------------------------------------------------------------------------------------------------------------------
void uart_putstr(UARTN_enum uartn, const int8 *str)
{
while(*str)
{
uart_putchar(uartn, *str++);
}
}
Printf函数打印
#include "headfile.h"
#pragma section all "cpu0_dsram"
uint16 num;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
printf("printf demon");
enableInterrupts();
while (TRUE)
{
num++;
printf("printf num :%dn", num);
systick_delay_ms(STM0, 100);
//注意事项1 printf打印的数据会通过串口发送出去
//注意事项2 printf所使用的串口和波特率可以在TC264_config.h文件中修改
//注意事项3 printf所使用的串口默认在get_clk函数中进行了初始化,如果不想使用printf则可以在TC264_config.h文件中将PRINTF_ENABLE宏定义设置为0即可
}
}
#pragma section all restore
GPIO触发中断
#include "headfile.h"
#pragma section all "cpu0_dsram"
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
//特别注意通道2 与 通道3都被摄像头占用,由于中断共用的关系,因此通道6 与 通道7也不能使用
//TC264的外部中断不像K60单片机那样所有IO都支持,TC264只有部分支持具体可以查看zf_eru.h中ERU_PIN_enum枚举定义
eru_init(ERU_CH0_REQ0_P15_4, RISING); //ERU通道0 使用P15_4引脚 上升沿中断
eru_init(ERU_CH4_REQ13_P15_5, RISING); //ERU通道4 使用P15_5引脚 上升沿中断
//通道0与4共用中断,中断内通过标志位判断
eru_init(ERU_CH1_REQ10_P14_3, RISING); //ERU通道1 使用P14_3引脚 上升沿中断
eru_init(ERU_CH5_REQ1_P15_8, RISING); //ERU通道5 使用P15_8引脚 上升沿中断
//通道1与5共用中断,中断内通过标志位判断
//中断函数在isr.c中
//函数名称分别为eru_ch0_ch4_isr 与 eru_ch1_ch5_isr
//中断相关的配置参数在isr_config.h内
//可配置参数有 ERU_CH0_CH4_INT_SERVICE 和 ERU_CH0_CH4_INT_PRIO
//ERU_CH0_CH4_INT_SERVICE 中断服务者,表示改中断由谁处理,0:CPU0 1:CPU1 3:DMA 不可设置为其他值
//ERU_CH0_CH4_INT_PRIO 中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//ERU其他中断也是同理
//需要特备注意的是 不可以有优先级相同的中断函数 每个中断的优先级都必须是不一样的
enableInterrupts();
while (TRUE)
{
//在isr.c的中断函数,函数定义的第二个参数固定为0,请不要更改,即使你用CPU1处理中断也不要更改,需要CPU1处理中断只需要在isr_config.h内修改对应的宏定义即可
//程序运行之后,使用杜邦线将P15_4、P15_5、P14_3、P15_8连接到GND,然后在断开,在连接如此往复
//将提示信息通过串口打印,可以先学习printf例程,了解如何使用printf
}
}
#pragma section all restore
eru初始化(gpio中断)
//-------------------------------------------------------------------------------------------------------------------
// @brief eru初始化(gpio中断)
// @param eru_pin 设置eru通道及引脚
// @param trigger 设置触发方式
// @return void
// Sample usage: eru_init(ERU_CH0_REQ0_P15_4, RISING);//eru通道0 使用P10_7引脚,上升沿触发中断
//-------------------------------------------------------------------------------------------------------------------
void eru_init(ERU_PIN_enum eru_pin, TRIGGER_enum trigger)
{
boolean interrupt_state = disableInterrupts();
IfxScu_Req_In *reqPin;
reqPin = eru_mux(eru_pin);
IfxScuEru_initReqPin(reqPin, IfxPort_InputMode_pullUp);
IfxScuEru_InputChannel inputChannel = (IfxScuEru_InputChannel)reqPin->channelId;
IfxScuEru_InputNodePointer triggerSelect = (IfxScuEru_InputNodePointer)(eru_pin/3);
IfxScuEru_OutputChannel outputChannel = (IfxScuEru_OutputChannel)(eru_pin/3);
switch(trigger)
{
case RISING:
{
IfxScuEru_disableFallingEdgeDetection(inputChannel);
IfxScuEru_enableRisingEdgeDetection(inputChannel);
}break;
case FALLING:
{
IfxScuEru_enableFallingEdgeDetection(inputChannel);
IfxScuEru_disableRisingEdgeDetection(inputChannel);
}break;
case BOTH:
{
IfxScuEru_enableFallingEdgeDetection(inputChannel);
IfxScuEru_enableRisingEdgeDetection(inputChannel);
}break;
default: ZF_ASSERT(FALSE);
}
IfxScuEru_enableTriggerPulse(inputChannel);
IfxScuEru_connectTrigger(inputChannel, triggerSelect);
IfxScuEru_setFlagPatternDetection(outputChannel, inputChannel, TRUE);
IfxScuEru_enablePatternDetectionTrigger(outputChannel);
IfxScuEru_setInterruptGatingPattern(outputChannel, IfxScuEru_InterruptGatingPattern_alwaysActive);
volatile Ifx_SRC_SRCR *src = &MODULE_SRC.SCU.SCU.ERU[(int)outputChannel % 4];
IfxSrc_Tos eru_service;
uint8 eru_priority;
switch((eru_pin/3)%4)
{
case 0:
{
eru_service = (IfxSrc_Tos)ERU_CH0_CH4_INT_SERVICE;
eru_priority = ERU_CH0_CH4_INT_PRIO;
}break;
case 1:
{
eru_service = (IfxSrc_Tos)ERU_CH1_CH5_INT_SERVICE;
eru_priority = ERU_CH1_CH5_INT_PRIO;
}break;
case 2:
{
eru_service = (IfxSrc_Tos)ERU_CH2_CH6_INT_SERVICE;
eru_priority = ERU_CH2_CH6_INT_PRIO;
}break;
case 3:
{
eru_service = (IfxSrc_Tos)ERU_CH3_CH7_INT_SERVICE;
eru_priority = ERU_CH3_CH7_INT_PRIO;
}break;
}
#pragma warning 507
IfxSrc_init(src, eru_service, eru_priority);
#pragma warning default
IfxSrc_enable(src);
restoreInterrupts(interrupt_state);
}
枚举ERU通道
typedef enum // 枚举ERU通道
{
//一个通道只能选择其中一个引脚作为 外部中断的输入
//例如通道0 可选引脚为P10_7 和 P15_4,
//在LQFP144封装中没有P10_7
ERU_CH0_REQ4_P10_7 = 0*3, ERU_CH0_REQ0_P15_4, //通道0可选引脚 LQFP没有P10_7引脚
//在LQFP144封装中没有P10_8
ERU_CH1_REQ5_P10_8 = 1*3, ERU_CH1_REQ10_P14_3, //通道1可选引脚 LQFP没有P10_8引脚
ERU_CH2_REQ7_P00_4 = 2*3, ERU_CH2_REQ14_P02_1, ERU_CH2_REQ2_P10_2, //通道2可选引脚
ERU_CH3_REQ6_P02_0 = 3*3, ERU_CH3_REQ3_P10_3, ERU_CH3_REQ15_P14_1, //通道3可选引脚
//通道4与通道0 共用中断函数 在中断内通过判断标志位来识别是哪个通道触发的中断
ERU_CH4_REQ13_P15_5 = 4*3, ERU_CH4_REQ8_P33_7, //通道4可选引脚
//通道5与通道1 共用中断函数
ERU_CH5_REQ1_P15_8 = 5*3, //通道5可选引脚
//通道6与通道2 共用中断函数
ERU_CH6_REQ12_P11_10 = 6*3, ERU_CH6_REQ9_P20_0, //通道6可选引脚
//通道7与通道3 共用中断函数
ERU_CH7_REQ16_P15_1 = 7*3, ERU_CH7_REQ11_P20_9, //通道7可选引脚
}ERU_PIN_enum;
//特别注意通道2 与 通道3都被摄像头占用,意味着只有两个外部中断可供用户使用
EEPROM(Dflash)保存参数
概述
在实际的应用中,保存在单片机RAM中的数据,掉电后就丢失了,保存在单片机的FLASH中的数据,又不能随意改变,也就是不能用它来记录变化的数值。但是在某些场合,我们又确实**需要记录下某些数据,而它们还时常需要改变或更新,掉电之后数据还不能丢失。**比如,我们的家用电表度数,电视机里边的频道记忆,一般都是使用EEPROM来保存数据,特点就是掉电后存储的数据不丢失。
一般情况下,EEPROM拥有30万到100万次的寿命,也就是它可以反复写入30-100万次,而读取次数是无限的。
24C02是一个常用的基于IIC通信协议的EEPROM元件,例如ATMEL公司的AT24C02、CATALYST公司的CAT24C02和ST公司的ST24C02等芯片。
IIC是一个通信协议,它拥有严密的通信时序逻辑要求,而EEPROM是一个元件,只是这个元件采样了IIC协议的接口与单片机相连而已,二者并没有必然的联系,EEPROM可以用其它接口,I2C也可以用在其它很多器件上。
#include "headfile.h"
#pragma section all "cpu0_dsram"
#define EXAMPLE_EEPROM_SECTOR (0)
uint8 write_data1;
uint16 write_data2;
uint32 write_data3;
float write_data4;
uint8 read_data1;
uint16 read_data2;
uint32 read_data3;
float read_data4;
uint32 write_buf;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
write_data1 = 66;
write_data2 = 6666;
write_data3 = 666666;
write_data4 = 6666.66;
//检查当前页是否有数据,如果有数据则需要擦除整个扇区,
if(flash_check(EXAMPLE_EEPROM_SECTOR, 0) || flash_check(EXAMPLE_EEPROM_SECTOR, 1) || flash_check(EXAMPLE_EEPROM_SECTOR, 2) || flash_check(EXAMPLE_EEPROM_SECTOR, 3))
{
eeprom_erase_sector(EXAMPLE_EEPROM_SECTOR);
}
//当写入的数据不是32位时,建议将数据放入uint32 类型的变量中,在将该变量作为参数传入到eeprom_page_program函数
write_buf = write_data1;
//将数据写入EXAMPLE_EEPROM_SECTOR扇区的第0页
eeprom_page_program(EXAMPLE_EEPROM_SECTOR, 0, &write_buf);
write_buf = write_data2;
eeprom_page_program(EXAMPLE_EEPROM_SECTOR, 1, &write_buf);
write_buf = write_data3;
eeprom_page_program(EXAMPLE_EEPROM_SECTOR, 2, &write_buf);
//保存浮点数的时候,使用float_conversion_uint32宏定义进行转换后在保存
write_buf = float_conversion_uint32(write_data4);
eeprom_page_program(EXAMPLE_EEPROM_SECTOR, 3, &write_buf);
//读取eeprom中的数据
read_data1 = flash_read(EXAMPLE_EEPROM_SECTOR, 0, uint8);
read_data2 = flash_read(EXAMPLE_EEPROM_SECTOR, 1, uint16);
read_data3 = flash_read(EXAMPLE_EEPROM_SECTOR, 2, uint32);
read_data4 = flash_read(EXAMPLE_EEPROM_SECTOR, 3, float);
printf("read_data1: %dn", read_data1);
printf("read_data2: %dn", read_data2);
printf("read_data3: %ldn", read_data3);
printf("read_data4: %fn", read_data4);
enableInterrupts();
while (TRUE)
{
}
}
#pragma section all restore
校验FLASH是否有数据
//-------------------------------------------------------------------------------------------------------------------
// @brief 校验FLASH是否有数据
// @param sector_num 需要写入的扇区编号 参数范围0-11
// @param page_num 当前扇区页的编号 参数范围0-1023
// @return 返回1有数据,返回0没有数据,如果需要对有数据的区域写入新的数据则应该对所在扇区进行擦除操作
// @since v1.0
// Sample usage: flash_check(0,0);//校验0号扇区,第0页是否有数据
//-------------------------------------------------------------------------------------------------------------------
uint8 flash_check(uint32 sector_num, uint32 page_num)
{
ZF_ASSERT(EEPROM_SECTOR_NUM>sector_num);
ZF_ASSERT(EEPROM_SECTOR_PAGE_NUM>page_num);
uint32 sector_addr = IfxFlash_dFlashTableEepLog[sector_num].start;
uint32 page_addr = sector_addr + page_num * IFXFLASH_DFLASH_PAGE_LENGTH;
uint32 temp = *(uint32 *)page_addr;
if(0 != temp) return 1;
return 0;
}
擦除扇区
//-------------------------------------------------------------------------------------------------------------------
// @brief 擦除扇区
// @param sector_num 需要写入的扇区编号 参数范围0-11
// @return void
// @since v1.0
// Sample usage: eeprom_erase_sector(0);
//-------------------------------------------------------------------------------------------------------------------
void eeprom_erase_sector(uint32 sector_num)
{
ZF_ASSERT(EEPROM_SECTOR_NUM>sector_num);
uint32 flash = 0;
uint16 end_init_sfty_pw;
uint32 sector_addr = IfxFlash_dFlashTableEepLog[sector_num].start;
end_init_sfty_pw = IfxScuWdt_getSafetyWatchdogPassword();
IfxScuWdt_clearSafetyEndinit(end_init_sfty_pw);
IfxFlash_eraseSector(sector_addr);
IfxScuWdt_setSafetyEndinit(end_init_sfty_pw);
IfxFlash_waitUnbusy(flash, IfxFlash_FlashType_D0);
}
编程一页
//-------------------------------------------------------------------------------------------------------------------
// @brief 编程一页
// @param sector_num 需要写入的扇区编号 参数范围0-11
// @param page_num 需要写入的页编号 参数范围0-1023
// @param buf 需要写入的数据地址 传入的数组类型必须为uint32
// @return void
// @since v1.0
// Sample usage: eeprom_page_program(0,0, &buf);
//-------------------------------------------------------------------------------------------------------------------
void eeprom_page_program(uint32 sector_num, uint32 page_num, uint32 *buf)
{
ZF_ASSERT(EEPROM_SECTOR_NUM>sector_num);
ZF_ASSERT(EEPROM_SECTOR_PAGE_NUM>page_num);
uint32 errors = 0;
uint32 flash = 0;
uint16 end_init_sfty_pw;
uint32 sector_addr = IfxFlash_dFlashTableEepLog[sector_num].start;
end_init_sfty_pw = IfxScuWdt_getSafetyWatchdogPassword();
uint32 page_addr = sector_addr + page_num * IFXFLASH_DFLASH_PAGE_LENGTH;
errors = IfxFlash_enterPageMode(page_addr);
ZF_ASSERT(0 == errors);
IfxFlash_waitUnbusy(flash, IfxFlash_FlashType_D0);
IfxFlash_loadPage(page_addr, buf[0], 0);
IfxScuWdt_clearSafetyEndinit(end_init_sfty_pw);
IfxFlash_writePage(page_addr);
IfxScuWdt_setSafetyEndinit(end_init_sfty_pw);
IfxFlash_waitUnbusy(flash, IfxFlash_FlashType_D0);
}
将浮点数转换为二进制数据
//-------------------------------------------------------------------------------------------------------------------
// @brief 将浮点数转换为二进制数据
// @param float_data 需要转换的浮点数
// @return 返回整型数据
// @since v1.0
// Sample usage: float_conversion_uint32(write_data4);
//-------------------------------------------------------------------------------------------------------------------
#define float_conversion_uint32(float_data) (*(uint32 *)&float_data)
uint8 flash_check(uint32 sector_num, uint32 page_num);
void eeprom_erase_sector(uint32 sector_num);
void eeprom_page_program(uint32 sector_num, uint32 page_num, uint32 *buf);
#endif
使用宏定义对flash进行数据读取
//-------------------------------------------------------------------------------------------------------------------
// @brief 使用宏定义对flash进行数据读取
// @param sector_num 需要写入的扇区编号
// @param page_num 需要读取的页
// @param type 读取的数据类型
// @return 返回给定地址的数据
// @since v1.0
// Sample usage: flash_read(0,0,uint32);//读取0号扇区 第0也数据 类型为uint32
//-------------------------------------------------------------------------------------------------------------------
#define flash_read(sector_num,page_num,type) (*(type *)((uint32)((EEPROM_BASE_ADDR + (sector_num)*EEPROM_SECTOR_SIZE) + (page_num*8))))
GTM模块输出PWM以及设定占空比
#include "headfile.h"
#pragma section all "cpu0_dsram"
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
gtm_pwm_init(ATOM0_CH4_P02_4, 50, 0);//ATOM 0模块的通道4 使用P02_4引脚输出PWM PWM频率50HZ 占空比百分之0/GTM_ATOM0_PWM_DUTY_MAX*100 GTM_ATOM0_PWM_DUTY_MAX宏定义在zf_gtm_pwm.h
gtm_pwm_init(ATOM0_CH5_P02_5, 1000, 0);
gtm_pwm_init(ATOM0_CH6_P02_6, 10000, 0);
gtm_pwm_init(ATOM0_CH7_P02_7, 30000, 0);
gtm_pwm_init(ATOM1_CH7_P00_8, 30000, 5000);
//每个通道都可以输出不同频率的PWM
pwm_duty(ATOM0_CH4_P02_4, 5000);//设置占空比为百分之5000/GTM_ATOM0_PWM_DUTY_MAX*100
pwm_duty(ATOM0_CH5_P02_5, 5000);
pwm_duty(ATOM0_CH6_P02_6, 5000);
pwm_duty(ATOM0_CH7_P02_7, 5000);
enableInterrupts();
while (TRUE)
{
}
}
#pragma section all restore
PWM初始化
//-------------------------------------------------------------------------------------------------------------------
// @brief PWM初始化
// @param pwmch PWM通道号及引脚
// @param freq PWM频率
// @param duty PWM占空比
// @return void
// Sample usage: gtm_pwm_init(ATOM0_CH7_P02_7, 50, 1000); //ATOM 0模块的通道7 使用P02_7引脚输出PWM PWM频率50HZ 占空比百分之1000/GTM_ATOM0_PWM_DUTY_MAX*100
// GTM_ATOM0_PWM_DUTY_MAX宏定义在zf_gtm_pwm.h 默认为10000
//-------------------------------------------------------------------------------------------------------------------
void gtm_pwm_init(ATOM_PIN_enum pwmch, uint32 freq, uint32 duty)
{
IfxGtm_Atom_Pwm_Config g_atomConfig;
IfxGtm_Atom_Pwm_Driver g_atomDriver;
IfxGtm_Atom_ToutMap *atom_channel;
atom_channel = gtm_atom_mux(pwmch);
switch(atom_channel->atom)
{
case 0: IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, duty <= GTM_ATOM0_PWM_DUTY_MAX); break;
case 1: IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, duty <= GTM_ATOM1_PWM_DUTY_MAX); break;
case 2: IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, duty <= GTM_ATOM2_PWM_DUTY_MAX); break;
case 3: IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, duty <= GTM_ATOM3_PWM_DUTY_MAX); break;
}
IfxGtm_enable(&MODULE_GTM);
if(!(MODULE_GTM.CMU.CLK_EN.U & 0x2))
{
IfxGtm_Cmu_setClkFrequency(&MODULE_GTM, IfxGtm_Cmu_Clk_0, CLK_FREQ);
IfxGtm_Cmu_enableClocks(&MODULE_GTM, IFXGTM_CMU_CLKEN_CLK0);
}
IfxGtm_Atom_Pwm_initConfig(&g_atomConfig, &MODULE_GTM);
g_atomConfig.atom = atom_channel->atom;
g_atomConfig.atomChannel = atom_channel->channel;
g_atomConfig.period = CLK_FREQ/freq;
g_atomConfig.pin.outputPin = atom_channel;
g_atomConfig.synchronousUpdateEnabled = TRUE;
switch(atom_channel->atom)
{
case 0: g_atomConfig.dutyCycle = (uint32)((uint64)duty * g_atomConfig.period / GTM_ATOM0_PWM_DUTY_MAX); break;
case 1: g_atomConfig.dutyCycle = (uint32)((uint64)duty * g_atomConfig.period / GTM_ATOM1_PWM_DUTY_MAX); break;
case 2: g_atomConfig.dutyCycle = (uint32)((uint64)duty * g_atomConfig.period / GTM_ATOM2_PWM_DUTY_MAX); break;
case 3: g_atomConfig.dutyCycle = (uint32)((uint64)duty * g_atomConfig.period / GTM_ATOM3_PWM_DUTY_MAX); break;
}
IfxGtm_Atom_Pwm_init(&g_atomDriver, &g_atomConfig);
IfxGtm_Atom_Pwm_start(&g_atomDriver, TRUE);
}
PWM占空比设置
//-------------------------------------------------------------------------------------------------------------------
// @brief PWM占空比设置
// @param pwmch PWM通道号及引脚
// @param duty PWM占空比
// @return void
// Sample usage: pwm_duty(ATOM0_CH7_P02_7, 5000);//设置占空比为百分之5000/GTM_ATOM0_PWM_DUTY_MAX*100
// GTM_ATOM0_PWM_DUTY_MAX宏定义在zf_gtm_pwm.h 默认为10000
//-------------------------------------------------------------------------------------------------------------------
void pwm_duty(ATOM_PIN_enum pwmch, uint32 duty)
{
uint32 period;
IfxGtm_Atom_ToutMap *atom_channel;
atom_channel = gtm_atom_mux(pwmch);
period = IfxGtm_Atom_Ch_getCompareZero(&MODULE_GTM.ATOM[atom_channel->atom], atom_channel->channel);
switch(atom_channel->atom)
{
case 0: duty = (uint32)((uint64)duty * period / GTM_ATOM0_PWM_DUTY_MAX); break;
case 1: duty = (uint32)((uint64)duty * period / GTM_ATOM1_PWM_DUTY_MAX); break;
case 2: duty = (uint32)((uint64)duty * period / GTM_ATOM2_PWM_DUTY_MAX); break;
case 3: duty = (uint32)((uint64)duty * period / GTM_ATOM3_PWM_DUTY_MAX); break;
}
IfxGtm_Atom_Ch_setCompareOneShadow(&MODULE_GTM.ATOM[atom_channel->atom], atom_channel->channel, duty);
}
GTM_ATOM0_PWM_DUTY_MAX宏定义
#define GTM_ATOM0_PWM_DUTY_MAX 10000 //GTM_ATOM0 PWM最大占空比 最大占空比越大占空比的步进值越小
#define GTM_ATOM1_PWM_DUTY_MAX 10000 //GTM_ATOM1 PWM最大占空比 最大占空比越大占空比的步进值越小
#define GTM_ATOM2_PWM_DUTY_MAX 10000 //GTM_ATOM2 PWM最大占空比 最大占空比越大占空比的步进值越小
#define GTM_ATOM3_PWM_DUTY_MAX 10000 //GTM_ATOM3 PWM最大占空比 最大占空比越大占空比的步进值越小
中断优先级设置
#include "headfile.h"
#pragma section all "cpu0_dsram"
uint16 adc_result;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//本例程没有代码
//中断优先级都在isr_config.h中 进行设置
//以及中断的服务者也在isr_config.h中 进行设置
//所有的中断 优先级务必保证不一样,不能有重复的优先级
//在uart、pit、GPIO中断例程中的备注都有提到
enableInterrupts();
while (TRUE)
{
//在isr.c的中断函数,函数定义的第二个参数固定为0,请不要更改,即使你用CPU1处理中断也不要更改,需要CPU1处理中断只需要在isr_config.h内修改对应的宏定义即可
}
}
#pragma section all restore
中断优先级及中断服务者
#ifndef _isr_config_h
#define _isr_config_h
//ISR_PRIORITY: TC264具有255个中断优先级可以设置 1-255,0优先级表示不开启中断,255为最高优先级
//特别注意
//中断优先级不能设置一样,所有中断优先级都必须设置为不一样的值
//特别注意
//INT_SERVICE: 宏定义决定中断由谁处理,也称为服务提供者(在TC264中,中断被叫做服务),可设置范围0:CPU0 1:CPU1 3:DMA 不可设置为其他值
//如果INT_SERVICE设置为3的话,ISR_PRIORITY的可设置范围则是0-47。
//------------PIT中断参数相关定义------------
#define CCU6_0_CH0_INT_SERVICE 0 //定义CCU6_0 PIT通道0中断服务类型,即中断是由谁响应处理 0:CPU0 1:CPU1 3:DMA 不可设置为其他值
#define CCU6_0_CH0_ISR_PRIORITY 30 //定义CCU6_0 PIT通道0中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
#define CCU6_0_CH1_INT_SERVICE 0
#define CCU6_0_CH1_ISR_PRIORITY 31
#define CCU6_1_CH0_INT_SERVICE 0
#define CCU6_1_CH0_ISR_PRIORITY 32
#define CCU6_1_CH1_INT_SERVICE 0
#define CCU6_1_CH1_ISR_PRIORITY 33
//------------GPIO中断参数相关定义------------
//通道0与通道4是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH0_CH4_INT_SERVICE 0 //定义ERU通道0和通道4中断服务类型,即中断是由谁响应处理 0:CPU0 1:CPU1 3:DMA 不可设置为其他值
#define ERU_CH0_CH4_INT_PRIO 40 //定义ERU通道0和通道4中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//通道1与通道5是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH1_CH5_INT_SERVICE 0 //定义ERU通道1和通道5中断服务类型,同上
#define ERU_CH1_CH5_INT_PRIO 41 //定义ERU通道1和通道5中断优先级 同上
//通道2与通道6是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH2_CH6_INT_SERVICE 3 //定义ERU通道2和通道6中断服务类型,同上
#define ERU_CH2_CH6_INT_PRIO 5 //定义ERU通道2和通道6中断优先级 同上
//通道3与通道7是公用一个中断函数 在中断内部通过标志位判断是谁触发的中断
#define ERU_CH3_CH7_INT_SERVICE 0 //定义ERU通道3和通道7中断服务类型,同上
#define ERU_CH3_CH7_INT_PRIO 43 //定义ERU通道3和通道7中断优先级 同上
//------------DMA中断参数相关定义------------
#define ERU_DMA_INT_SERVICE 0 //ERU触发DMA中断服务类型,即中断是由谁响应处理 0:CPU0 1:CPU1 3:DMA 不可设置为其他值
#define ERU_DMA_INT_PRIO 60 //ERU触发DMA中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//------------串口中断参数相关定义------------
#define UART0_INT_SERVICE 0 //定义串口0中断服务类型,即中断是由谁响应处理 0:CPU0 1:CPU1 3:DMA 不可设置为其他值
#define UART0_TX_INT_PRIO 10 //定义串口0发送中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
#define UART0_RX_INT_PRIO 11 //定义串口0接收中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
#define UART0_ER_INT_PRIO 12 //定义串口0错误中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
#define UART1_INT_SERVICE 0
#define UART1_TX_INT_PRIO 13
#define UART1_RX_INT_PRIO 14
#define UART1_ER_INT_PRIO 15
#define UART2_INT_SERVICE 0
#define UART2_TX_INT_PRIO 16
#define UART2_RX_INT_PRIO 17
#define UART2_ER_INT_PRIO 18
#define UART3_INT_SERVICE 0
#define UART3_TX_INT_PRIO 19
#define UART3_RX_INT_PRIO 20
#define UART3_ER_INT_PRIO 21
#endif
系统定时器完成延时与计时功能
#include "headfile.h"
#pragma section all "cpu0_dsram"
uint16 time;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
enableInterrupts();
while (TRUE)
{
systick_start(STM1); //使用STM1 进行计时
systick_delay_ms(STM0, 100); //延时100MS 使用STM0定时器 也可以使用STM1定时器
time = systick_getval_ms(STM1); //读取STM1计时时间
printf("delay time: %dmsn", time);
}
}
#pragma section all restore
systick定时器启动
//-------------------------------------------------------------------------------------------------------------------
// @brief systick定时器启动
// @param stmn 选择使用的模块
// @return void
// Sample usage: systick_start(STM0);//记录下当前的时间
//-------------------------------------------------------------------------------------------------------------------
void systick_start(STMN_enum stmn)
{
systick_count[stmn] = IfxStm_getLower(IfxStm_getAddress((IfxStm_Index)stmn));
}
systick延时函数
//-------------------------------------------------------------------------------------------------------------------
// @brief systick延时函数
// @param time 需要延时的时间
// @return void
// Sample usage: 无需用户调用,用户请使用h文件中的宏定义
//-------------------------------------------------------------------------------------------------------------------
void systick_delay(STMN_enum stmn, uint32 time)
{
uint32 stm_clk;
stm_clk = IfxStm_getFrequency(IfxStm_getAddress((IfxStm_Index)stmn));
IfxStm_waitTicks(IfxStm_getAddress((IfxStm_Index)stmn), (uint32)((uint64)stm_clk*time/1000000000));
}
获得当前System tick timer的值
//-------------------------------------------------------------------------------------------------------------------
// @brief 获得当前System tick timer的值
// @param stmn 选择使用的模块
// @return uint32 返回从开始到现在的时间(单位10ns)
// Sample usage: uint32 tim = systick_getval(STM0);
//-------------------------------------------------------------------------------------------------------------------
uint32 systick_getval(STMN_enum stmn)
{
uint32 time;
uint32 stm_clk;
stm_clk = IfxStm_getFrequency(IfxStm_getAddress((IfxStm_Index)stmn));
time = IfxStm_getLower(IfxStm_getAddress((IfxStm_Index)stmn)) - systick_count[stmn];
time = (uint32)((uint64)time * 100000000 / stm_clk);
return time;
}
单片机的双核运行
#include "headfile.h"
#pragma section all "cpu0_dsram"
uint16 adc_result;
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
//双核的使用其实很简单,就是在两个main中编写不用的程序即可
//本例程使用核心0 闪烁P20_8的LED 核心1闪烁P20_9的LED
gpio_init(P20_8, GPO, 0, PUSHPULL);
enableInterrupts();
while (TRUE)
{
gpio_toggle(P20_8);
systick_delay_ms(STM0, 100);
}
}
#pragma section all restore
#include "headfile.h"
#pragma section all "cpu1_dsram"
void core1_main(void)
{
disableInterrupts();
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
//用户在此处调用各种初始化函数等
//双核的使用其实很简单,就是在两个main中编写不用的程序即可
//本例程使用核心0 闪烁P20_8的LED 核心1闪烁P20_9的LED
gpio_init(P20_9, GPO, 0, PUSHPULL);
enableInterrupts();
while (TRUE)
{
//用户在此处编写任务代码
gpio_toggle(P20_9);
systick_delay_ms(STM1, 100);
}
}
#pragma section all restore
GPIO初始化
//-------------------------------------------------------------------------------------------------------------------
// @brief GPIO初始化
// @param pin 选择的引脚 (可选择范围由 common.h 内PIN_enum枚举值确定)
// @param dir 引脚的方向 输出:GPO 输入:GPI
// @param dat 引脚初始化时设置的电平状态,输出时有效 0:低电平 1:高电平
// @param pinmode 引脚配置(可设置参数由zf_gpio.h文件内GPIOMODE_enum枚举值确定)
// @return void
// Sample usage: gpio_init(P00_0, GPO, 1, PUSHPULL);//P00_0初始化为GPIO功能、输出模式、输出高电平、推挽输出
// @note 需要特别注意P20_2是不能用于输出的,仅仅只有输入的功能
//-------------------------------------------------------------------------------------------------------------------
void gpio_init(PIN_enum pin, GPIODIR_enum dir, uint8 dat, GPIOMODE_enum pinmode)
{
IfxPort_Mode port_mode;
if(dir == GPI)
{
switch(pinmode)
{
case NO_PULL: port_mode = IfxPort_Mode_inputNoPullDevice; break;
case PULLUP: port_mode = IfxPort_Mode_inputPullUp; break;
case PULLDOWN: port_mode = IfxPort_Mode_inputPullDown; break;
default: port_mode = IfxPort_Mode_inputPullUp; break;
}
}
else
{
switch(pinmode)
{
case OPENDRAIN: port_mode = IfxPort_Mode_outputOpenDrainGeneral; break;//开漏输出
default: port_mode = IfxPort_Mode_outputPushPullGeneral; break;//默认为推挽输
}
}
IfxPort_setPinMode(get_port(pin), pin&0x1f, port_mode);
IfxPort_setPinPadDriver(get_port(pin), pin&0x1f, IfxPort_PadDriver_cmosAutomotiveSpeed1);
if(dir == GPO)
{
if(dat) IfxPort_setPinHigh(get_port(pin), pin&0x1f);
else IfxPort_setPinLow(get_port(pin), pin&0x1f);
}
}
GPIO 翻转
//-------------------------------------------------------------------------------------------------------------------
// @brief GPIO 翻转
// @param pin 选择的引脚 (可选择范围由 common.h 内PIN_enum枚举值确定)
// @return void
// Sample usage: gpio_toggle(P00_0);//P00_0引脚电平翻转
//-------------------------------------------------------------------------------------------------------------------
void gpio_toggle(PIN_enum pin)
{
IfxPort_togglePin(get_port(pin), pin&0x1f);
}
使用ILLD进行软件FFT计算
#include "headfile.h"
#include "SysSe/Math/Ifx_FftF32.h"
#pragma section all "cpu0_dsram"
#define SAMPLE_FREQUENCY 100 //定义采样频率
#define SIZE_N 2000 //定义采集数量
cfloat32 fft_in[SIZE_N];
cfloat32 fft_out[SIZE_N];
uint32 use_time;
//本例程专门配套编写了matlab代码
//对函数y = 1024*(cos(3*pi*x))+512*(cos(7*pi*x)+pi/2)+2047做FFT运算
//然后绘制出幅频图像,可以在工程目录下找到FFT.m文件,使用matlab打开并运行可以看到绘制的幅频图像
int core0_main(void)
{
int i;
double x1, x2;
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
enableInterrupts();
for (i=0; i<SIZE_N; i++) //生成输入信号
{
//函数为y = 1024*(cos(3*pi*x))+512*(cos(7*pi*x)+pi/2)+2047
//计算x
x1 = 3 * IFX_PI * (double)i / SAMPLE_FREQUENCY;
x2 = (7 * IFX_PI * (double)i / SAMPLE_FREQUENCY) + (IFX_PI / 2);
//代入x计算y
fft_in[i].real =(float32)(1024 * cosf(x1) + 512 * cosf(x2) + 2047);
fft_in[i].imag =0.0;
}
while (TRUE)
{
systick_start(STM0); //开始计时
Ifx_FftF32_radix2(fft_out, fft_in, SIZE_N); //进行FFT运算
//Ifx_FftF32_radix2I 为IFT(FFT逆变换)运算
use_time = systick_getval_us(STM0); //获取计时时间
//将结果通过串口打印,可以先学习printf例程,了解如何使用printf
printf("use_time: %ldusn", use_time); //打印fft一次耗时多久
}
}
#pragma section all restore
指定变量或代码位置
RAM
RAM :随机存取存储器(random access memory,RAM)又称作“随机存储器”。
是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。
在计算机的组成结构中,有一个很重要的部分,就是存储器。存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。存储器的种类很多,按其用途可分为主存储器和辅助存储器[或者内存储器和外存储器,主存储器简称内存。内存在电脑中起着举足轻重的作用。内存一般采用半导体存储单元。
因为RAM是内存其中最重要的存储器,所以通常我们直接称之为内存。
#include "headfile.h"
//如果是新手可以先不学习本例程,本例程主要针对单片机基础较好的同学,想自己优化RAM分配提高性能的同学参考学习
//学习的时候请从上往下认真阅读。
//在TC264中的两个核心,实际上他们的流水线数量是不一样的,CPU0是一级流水线,CPU1是三级流水线,因此CPU1更适合做运算,速度会比CPU0快一些。
//TC264D单片机有如下几个RAM可供用户使用
//CPU0 psram 程序RAM 大小为16KB
//CPU0 dsram 数据RAM 大小为72KB
//CPU1 psram 程序RAM 大小为32KB
//CPU1 dsram 数据RAM 大小为120KB
//TC264D中两个核心都有自己的程序缓存
//CPU0 Cache大小为8KB
//CPU1 Cache大小为16KB
//程序RAM用来放代码,将代码放在RAM中,可以提高执行速度,特别是当周期运行的代码大小超过了cache之后效果比较显著。
//数据RAM用来放变量,任意CPU都可以访问dsram0和dsram1,也就是说CPU0可以访问dsram1,这种行为叫做跨核访问,带来的问题就是访问延迟有所增大
//最好的是CPU0访问dsram0,CPU1访问dsram1,这样速度最快。
//默认情况下定义的全局变量都是保存在dsram1中的,这就会导致在CPU0中跑的代码对RAM读取会有一定的延迟
//那么接下来我们就要学习如何指定变量存放位置
//使用#pragma section all "cpu0_dsram" 和 #pragma section all restore可以将变量放在cpu0_dsram
//我们只需要将变量定义放在上面两句话中间即可,使用示例如下
#pragma section all "cpu0_dsram"
uint8 test_arry[5]; //将变量放在cpu0_dsram区域内
//如果将cpu0_dsram改为cpu1_dsram 则变量放在cpu1_dsram区域内
//我们建议程序由哪个CPU执行,则将所使用的变量放置在哪个区域内,默认不指定的情况下变量被放置在cpu1_dsram区域
#pragma section all restore
//使用#pragma section all "cpu0_psram" 和 #pragma section all restore可以将函数加载到cpu0_psram
//我们只需要将函数定义放在上面两句话中间即可,使用示例如下
#pragma section all "cpu0_psram"
void delay_tset(void) //将函数加载到cpu0_psram区域内执行
{
int i;
i = 999;
while(i--);
}
//如果将cpu0_psram改为cpu1_psram 则函数加载到cpu1_psram区域内
//我们建议程序由哪个CPU执行,则将函数加载到在哪个区域内,默认不指定的情况下,程序都是从flash加载并执行
//通常我们都不需要将函数放在RAM运行,一般在周期执行的程序大小超过了cache后,会出现程序执行速度降低,这个时候我们就可以将部分代码放入RAM执行,可以提高运算速度
#pragma section all restore
//如果在写代码的时候忘记了上面的使用方式,可以从LibrariesDoc文件夹内找到version.txt,打开后可以看到上面的使用示例
int core0_main(void)
{
get_clk();//获取时钟频率 务必保留
test_arry[0] = 0;
delay_tset();
enableInterrupts();
while (TRUE)
{
//如果是新手可以先不学习本例程,本例程主要针对单片机基础较好的同学,想自己优化RAM分配提高性能的同学参考学习
//如果是移植代码请务必从最新版本的库,提取LSL文件替换到自己的工程
//学习的时候请从上往下认真阅读。
}
}
使用CPU1处理中断
#include "headfile.h"
#pragma section all "cpu0_dsram"
//将本语句与#pragma section all restore语句之间的全局变量都放在CPU0的RAM中
//工程导入到软件之后,应该选中工程然后点击refresh刷新一下之后再编译
//工程默认设置为关闭优化,可以自己右击工程选择properties->C/C++ Build->Setting
//然后在右侧的窗口中找到C/C++ Compiler->Optimization->Optimization level处设置优化等级
//一般默认新建立的工程都会默认开2级优化,因此大家也可以设置为2级优化
//对于TC系列默认是不支持中断嵌套的,希望支持中断嵌套需要在中断内使用enableInterrupts();来开启中断嵌套
//简单点说实际上进入中断后TC系列的硬件自动调用了disableInterrupts();来拒绝响应任何的中断,因为需要我们自己手动调用enableInterrupts();来开启中断的响应。
int core0_main(void)
{
disableInterrupts();
get_clk();//获取时钟频率 务必保留
//用户在此处调用各种初始化函数等
//特别注意通道2 与 通道3都被摄像头占用,由于中断共用的关系,因此通道6 与 通道7也不能使用
//TC264的外部中断不像K60单片机那样所有IO都支持,TC264只有部分支持具体可以查看zf_eru.h中ERU_PIN_enum枚举定义
eru_init(ERU_CH0_REQ0_P15_4, RISING); //ERU通道0 使用P15_4引脚 上升沿中断
eru_init(ERU_CH4_REQ13_P15_5, RISING); //ERU通道4 使用P15_5引脚 上升沿中断
//通道0与4共用中断,中断内通过标志位判断
eru_init(ERU_CH1_REQ10_P14_3, RISING); //ERU通道1 使用P14_3引脚 上升沿中断
eru_init(ERU_CH5_REQ1_P15_8, RISING); //ERU通道5 使用P15_8引脚 上升沿中断
//通道1与5共用中断,中断内通过标志位判断
//中断函数在isr.c中
//函数名称分别为eru_ch0_ch4_isr 与 eru_ch1_ch5_isr
//中断相关的配置参数在isr_config.h内
//可配置参数有 ERU_CH0_CH4_INT_SERVICE 和 ERU_CH0_CH4_INT_PRIO
//ERU_CH0_CH4_INT_SERVICE 中断服务者,表示改中断由谁处理,0:CPU0 1:CPU1 3:DMA 不可设置为其他值
//ERU_CH0_CH4_INT_PRIO 中断优先级 优先级范围1-255 越大优先级越高 与平时使用的单片机不一样
//本例程ERU_CH0_CH4_INT_SERVICE默认配置为1了 也就是由CPU1处理
//ERU其他中断也是同理
//需要特备注意的是 不可以有优先级相同的中断函数 每个中断的优先级都必须是不一样的
enableInterrupts();
while (TRUE)
{
//在isr.c的中断函数,函数定义的第二个参数固定为0,请不要更改,即使你用CPU1处理中断也不要更改,需要CPU1处理中断只需要在isr_config.h内修改对应的宏定义即可
//程序运行之后,使用杜邦线将P15_4、P15_5、P14_3、P15_8连接到GND,然后在断开,在连接如此往复
//在FSS窗口会看到对应的提示信息
}
}
#pragma section all restore
eru初始化(gpio中断)
//-------------------------------------------------------------------------------------------------------------------
// @brief eru初始化(gpio中断)
// @param eru_pin 设置eru通道及引脚
// @param trigger 设置触发方式
// @return void
// Sample usage: eru_init(ERU_CH0_REQ0_P15_4, RISING);//eru通道0 使用P10_7引脚,上升沿触发中断
//-------------------------------------------------------------------------------------------------------------------
void eru_init(ERU_PIN_enum eru_pin, TRIGGER_enum trigger)
{
boolean interrupt_state = disableInterrupts();
IfxScu_Req_In *reqPin;
reqPin = eru_mux(eru_pin);
IfxScuEru_initReqPin(reqPin, IfxPort_InputMode_pullUp);
IfxScuEru_InputChannel inputChannel = (IfxScuEru_InputChannel)reqPin->channelId;
IfxScuEru_InputNodePointer triggerSelect = (IfxScuEru_InputNodePointer)(eru_pin/3);
IfxScuEru_OutputChannel outputChannel = (IfxScuEru_OutputChannel)(eru_pin/3);
switch(trigger)
{
case RISING:
{
IfxScuEru_disableFallingEdgeDetection(inputChannel);
IfxScuEru_enableRisingEdgeDetection(inputChannel);
}break;
case FALLING:
{
IfxScuEru_enableFallingEdgeDetection(inputChannel);
IfxScuEru_disableRisingEdgeDetection(inputChannel);
}break;
case BOTH:
{
IfxScuEru_enableFallingEdgeDetection(inputChannel);
IfxScuEru_enableRisingEdgeDetection(inputChannel);
}break;
default: ZF_ASSERT(FALSE);
}
IfxScuEru_enableTriggerPulse(inputChannel);
IfxScuEru_connectTrigger(inputChannel, triggerSelect);
IfxScuEru_setFlagPatternDetection(outputChannel, inputChannel, TRUE);
IfxScuEru_enablePatternDetectionTrigger(outputChannel);
IfxScuEru_setInterruptGatingPattern(outputChannel, IfxScuEru_InterruptGatingPattern_alwaysActive);
volatile Ifx_SRC_SRCR *src = &MODULE_SRC.SCU.SCU.ERU[(int)outputChannel % 4];
IfxSrc_Tos eru_service;
uint8 eru_priority;
switch((eru_pin/3)%4)
{
case 0:
{
eru_service = (IfxSrc_Tos)ERU_CH0_CH4_INT_SERVICE;
eru_priority = ERU_CH0_CH4_INT_PRIO;
}break;
case 1:
{
eru_service = (IfxSrc_Tos)ERU_CH1_CH5_INT_SERVICE;
eru_priority = ERU_CH1_CH5_INT_PRIO;
}break;
case 2:
{
eru_service = (IfxSrc_Tos)ERU_CH2_CH6_INT_SERVICE;
eru_priority = ERU_CH2_CH6_INT_PRIO;
}break;
case 3:
{
eru_service = (IfxSrc_Tos)ERU_CH3_CH7_INT_SERVICE;
eru_priority = ERU_CH3_CH7_INT_PRIO;
}break;
}
#pragma warning 507
IfxSrc_init(src, eru_service, eru_priority);
#pragma warning default
IfxSrc_enable(src);
restoreInterrupts(interrupt_state);
}
电磁寻迹文件阅读
#include "headfile.h"
#define ADC_LEFT_CHANNEL ADC1_CH3_B14//定义左电感通道
#define ADC_RIGHT_CHANNEL ADC1_CH4_B15//定义右电感通道
uint8 ad_left;
uint8 ad_right;
int16 ad_sum;
int16 ad_diff;
int16 position;
int main(void)
{
DisableGlobalIRQ();
board_init();//务必保留,本函数用于初始化MPU 时钟 调试串口
adc_init(ADC_1,ADC_LEFT_CHANNEL,ADC_8BIT);//初始化B14引脚为ADC功能,分辨率为8位
adc_init(ADC_1,ADC_RIGHT_CHANNEL,ADC_8BIT);//初始化B15引脚为ADC功能,分辨率为8位
EnableGlobalIRQ(0);
while (1)
{
ad_left = adc_mean_filter(ADC_1,ADC_LEFT_CHANNEL,5);//采集左电感数值 采集5次求平均
ad_right = adc_mean_filter(ADC_1,ADC_RIGHT_CHANNEL,5);//采集右电感数值 采集5次求平均
ad_sum = ad_left + ad_right;//计算电感之和
ad_diff = (int16)ad_left - ad_right;//计算电感之差
if(ad_sum>30)//边界保护
{
//方法一
position = (ad_diff*100)/(ad_sum+1);//计算差比和数值 并放大一百倍
//方法二
position = (ad_diff<<7)/(ad_sum+1); //计算差比和数值 并放大128倍
systick_delay_ms(5);//延时5ms
}
}
}
编程要点
一
对于电磁寻迹通常8位adc分辨率足够使用,因此示例代码中初始化adc时选择了8位的分辨率,因此ad_left与ad_right定义的是8位无符号类型。
在前面的差比和计算中我们已经看到了计算出来的结果是有正有负的,因此position定义的是有符号16位类型,需要注意的是在计算差的时候切记进行强制转换为有符号之后再进行计算,也可以直接将ad_left与ad_right定义为有符号,但是就需要定义为有符号16位类型,否则就可能无法存储adc采集函数返回的数值,因为8位adc分辨率返回的数值范围是0-255。
二
程序当中有做边界保护,为什么要做边界保护呢?首先我们来假设一下车模在运行的过程中由于打滑或者弯道较急,导致导致电感的位置偏离赛道较多,从而导致两个电感的值都比较小。例如电感1为10,电感2为15,那么差比和计算出来后结果等于-0.2,实际上车模位置已经偏离赛道较大而计算出来的结果却偏小,因此我们采用电感的和值进行一定的限制,如果小于一定的值则使用保留上次的结果,不计算也就不会改变position的值也就是保留了上次的结果。
边界保护处理算法:
边界保护的和值的确定可以采用将车模从赛道中心往任意一边远离,然后观察计算出来的position,position应该是逐渐变大然后慢慢变小,在从大变小的转折点记录下此时的和值,然后将此和值设置为边界保护来使用,根据实际的运行情况可以在此基础上进行调整。
三
可以看到程序中的差比和计算公式稍微有一点不一样,为了避免除数为0,因此和值固定加一了,然后由于有边界保护的原因实际上和值不可能为0的,主要在还没有引入边界保护时使用。
四
程序中我们可以看到差比和有两个计算公式,区别在于一个放大了100倍,一个放大了128倍,首先为啥我们要对结果进行放大呢,在最开始的时候我们已经看见了,计算出来的结果都是小于1的,从公式原理上我们也可以看出来,差比和计算出来的结果只能是-1至1,为了直接保存结果我们就只能使用float变量去保存结果,为了提高单片机的运算速度因此我们先对差进行了放大,然后直接使用整数类型运算,将结果变换为-100至100。为什么写了两种放大方法呢,首先第一种因为是放大100倍便于思考的时候计算简单,但是由于需要使用乘法单片机运算速度会稍微慢一点,因此我们还可以采用环移的方式来放大,左环移7位也就是放大了128倍比较接近100。通过这样的方法可以进一步的降级运算量,提高运算速度。需要特别注意,由于计算的过程中都是使用的整数,不论是使用左环移还是乘法进行放大时,务必先放大之后再除,否则会出现1/2*100=0的结果。由于是整数类型的运算1/2是等于0的,所以会出现错误的结果。
五
对于电感采集与偏差计算实际上并不是放在主循环内一直运行的,一般都是将这部分代码和舵机PD控制代码一起放在pit周期中断内运行的。至此通过电感来计算车模的位置已经结束了,当然这仅仅是一个初级的办法供参考学习,其他方法还需要自己一步一步探索。根据计算出来的位置信息就可以对车模的转向和速度进行控制了,这样就可以实现让车模跟随赛道前进,实现寻迹的效果。
智能车竞赛线上培训
PWM波形如何生成
PWM简介
脉冲宽度调制(Pulse Width Modulation,简称PWM)是一种对模拟信号电平进行数字编码的方法。
通过高分辨率计数器的使用,用方波不同的占空比用来对一个具体模拟信号的电平进行编码。
PWM波----周期不变,占空比可调。
pwm的频率:是指1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期),也就是说一秒钟PWM有多少个周期。
单位: Hz
表示方式: 50Hz 100Hz
pwm的周期:T=1/f
占空比:是一个脉冲周期内,高电平的时间与整个周期时间的比例
单位: % (0%-100%)
表示方式:20%
定时器简介
单片机中的定时器和计数器其实是同一个物理的电子元件,只不过计数器记录的是单片机外部 发生的事情(接受的是外部脉冲),而定时器则是由单片机自身提供的一个非常稳定的计数器,这个稳定的计数器就是晶振部件产生稳定的时钟信号触发计数器而得来。
TC264时钟信号简介
GTM和ATOM模块说明
它包含具有不同功能子模块的模块框架。这些子模块可以以可配置的方式组合以形成一个复杂的计时器模块,该模块为一个应用程序域中的不同应用程序域和不同类提供服务。 由于具有可扩展性和可配置性,因此将计时器称为通用计时器。
TC264 GTM时钟信号
CMU产生3个子模块
- CMU_CLK(可配置频率) :ATOM/TIM
- FXCLK(固定分频) : TOM
- 外部时钟
TC264 GTM ATOM介绍
连接到ARU的定时器输出模块 (ATOM)可以连接到ARU,因此无需CPU的干预即可生成复杂的输出信号。
典型地,通过ARU连接通过例如连接到ARU的子模块提供输出信号特性。 MCS,DPLL或PSM。
每个ATOM子模块包含八个输出通道, 它们可以在几种可配置的操作模式下彼此独立地操作。
更新占空比和周期的方式,有两种同步更新和异步更新 。我们一般用同步更新。占空比、周期的同步变化意味着在前一个周期结束之后, 占空比或周期持续时间发生变化。
同步通信和异步通信
首先是两者的不同
- 同步通信要求接收端时钟频率和发送端时钟频率一致,发送端发送连续的比特流;异步通信时不要求接收端时钟和发送端时钟同步,发送端发送完一个字节后,可经过任意长的时间间隔再发送下一个字节。
- 同步通信效率高;异步通信效率较低。
- 同步通信较复杂,双方时钟的允许误差较小;异步通信简单,双方时钟可允许一定误差。
- 同步通信可用于点对多点;异步通信只适用于点对点。
异步通信
- 异步通信中的接收方并不知道数据什么时候会到达,收发双方可以有各自自己的时钟。发送方发送的时间间隔可以不均,接收方是在数据的起始位和停止位的帮助下实现信息同步的。这种传输通常是很小的分组,比如一个字符为一组,为这个组配备起始位和结束位。所以这种传输方式的效率是比较低的,毕竟额外加入了很多的辅助位作为负载,常用在低速的传输中。
- 以RS232协议规定为例,异步通信一个字符一个字符地传输,每个字符一位一位地传输,并且传输一个字符时,总是以“起始位”开始(低电平,逻辑值0),以“停止位”结束,字符之间没有固定的时间间隔要求。字符数据本身由5~8位数据位组成,接着字符后面是一位校验位(也可以没有校验位),最后是一位或一位半或二位停止位,停止位后面是不定长的空闲位。停止位和空闲位都规定为高电平(逻辑值1),这样就保证起始位开始处一定有一个下跳沿。
- 举个例子,我们的键盘按下一个按键,发出一个字符信号,异步传输机制就会为它加上前后的辅助同步信息,帮助接收方识别到我们按下了哪一个按键。因为我们敲击键盘的节奏不固定,所以异步是一种很适合的方式。
同步通信
- 同步通信中双方使用频率一致的时钟,它的分组相比异步则大得多,称为一个数据帧,通过独特的bit串作为启停标识。发送方要以固定的节奏去发送数据,而接收方要时刻做好接收数据的准备,识别到前导码后马上要开始接收数据了。同步这种方式中因为分组很大,很长一段数据才会有额外的辅助位负载,所以效率更高,更加适合对速度要求高的传输,当然这种通信对时序的要求也更高。
- 同步通信是一种连续串行传送数据的通信方式,一次通信只传送一帧信息,由同步字符、数据字符和校验字符(CRC)组成。
中断有两个:
在CN0等于CM1时,触发中断。
在CN0等于CM0时,触发中断。
模数转换
ADC基本性能
TC26x提供了一系列模拟输入通道,这些通道使用逐次逼近寄存器(SAR) 原理连接到模拟/数字转换器的群集,以将模拟输入值(电压)转换为离散的数字值。
- 最多4个独立的转换器,每个转换器最多16个模拟输入通道。
- 转换时间低于1 µs(取决于结果宽度和采样时间)
- 灵活的来源选择和仲裁
- 可编程的任意转换序列(单个或重复)
- 每组中可配置的自动扫描转换(单个或重复)
- 在后台(所有转换器)中可配置的自动扫描转换(单个或重复)
- 8/10/12位的可选结果宽度
- 独立的结果寄存器
请求控制单元
TC275的ADC模块共提供3种请求源,即顺序请求源、扫描请求源和后台请求源。
每个通道可以放到顺序请求源或者扫描请求源中,如果某通道既没有在顺序请求源,也没有在扫描请求源中,那么该通道被放到后台请求源中。
转换控制单元
主要设置采样时间、 转换精度和参考电压等参数。
采样过程
串口通信
通讯概念简介
同步通信
发送端在发送串行数据的同时,提供一个时钟信号,并按照一定的约定(例如:在时钟信号的上升沿的时候,将数据发送出去)发送数据,接收端根据发送端提供的时钟信号,以及大家的约定,接收数据。
异步通信
接收方并不知道数据什么时候会到达,收发双方可以有各自自己的时钟。发送方发送的时间间隔可以不均,接收方是在数据的起始位和停止位的帮助下实现信息同步的。这种传输通常是很小的分组。
SPI通信
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM、FLASH、实时时钟、AD转换器等器件之间的通信。它在芯片中只占用四根管脚 (Pin) 用来控制以及数据传输, 节约了芯片的引脚数目, 同时为 PCB在布局上节省了空间. 正是出于这种简单易用的特性, 现在越来越多的芯片上都集成了 SPI技术。
UART通信
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,通常称作UART) 是一种串行异步收发协议,应用十分广泛。UART工作原理是将数据的二进制位一位一位的进行传输。在UART通讯协议中信号线上的状态位高电平代表’1’低电平代表’0’。当然两个设备使用UART串口通讯时,必须先约定好传输速率和一些数据位。
ASCLIN模块简介
ASCLIN模块的主要目的是仅使用数据输入 (RX),数据输出(TX)信号提供与外部设备的异步串行通信。 该模块的重点是快速灵活的通信:使用LIN协议的快速点对点或主机对多从机通信。此外,该模块支持同步SPI通信。
基本工作过程说明
如何读取编码器信号
简述增量编码
编码器(encoder)
是将信号(如比特流)或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺。 按照工作原理编码器可分为增量式和绝对式两类。
增量式编码器
是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。
增量式的两种信号模式
- A、B、Z
- A/B、DIR、Z
绝对式编码器
绝对式编码器的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。
单圈绝对式编码器
旋转单圈绝对式编码器,以转动中测量光码盘各道刻线,以获取唯一的编码,当转动超过360度时,编码器又回到原点,这样就不符合绝对编码唯一的原则,这样的编码器只能用于旋转范围360度以内的测量,称为单圈绝对式编码器。
多圈绝对式编码器
测量旋转超过360度范围,就要使用多圈绝对式编码器。
编码器生产厂家运用钟表齿轮机械原理,当中心码盘旋转时,通过齿轮传动另一组码盘(或多齿轮,多组码盘),在单圈编码的基础上再增加圈数的编码,以扩大编码器的测量范围,这样的绝对编码器就称为多圈式绝对编码器。
GPT12模块介绍
通用定时器单元模块GPT1和 GPT2具有非常灵活的多功能定时器结构,可用于定时,事件计数,脉冲宽度测量,脉冲生成,倍频和其他目的。
它们包含五个16位定时器,这些定时器被分组到两个定时器模块GPT1和GPT2中。 每个块中的每个定时器都可以在多种不同模式下独立运行,例如门控定时器或计数器模式,或者可以与同一块中的另一个定时器串联。
GPT1:T2/T3/T4功能
- 计时器模式
- 门控定时器模式
- 计数器模式
- 增量接口模式
GPT2:T5/T6功能
- 计时器模式
- 门控定时器模式
- 计数器模式
引脚说明
计数模式
增量编码模式
最后
以上就是开放可乐为你收集整理的第三周铁人战队学习总结ADC环境例程与库函数学习电磁寻迹文件阅读智能车竞赛线上培训的全部内容,希望文章能够帮你解决第三周铁人战队学习总结ADC环境例程与库函数学习电磁寻迹文件阅读智能车竞赛线上培训所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复