【征文活动】基于KL25芯片 飞思卡尔芯片 使用心得
【背景】参与论坛的 《飞思卡尔KL25开源项目》有幸参与了论坛的飞思卡尔KL25开源活动,http://www.amobbs.com/thread-5610374-1-1.html 开始真正接触到飞思卡尔的芯片。
下边这是苏州大学的板子,相信好些参与活动的人都有。
以前上家公司也用过飞思卡尔的芯片 MC90S 系列的,当时的印象是一个字贵,两个字高大,三个字就是高大上了,呵呵。当时看了下该芯片的环境是专用的,仿真器也是专用的。在AVR盛行的时候,个人感觉个人玩的话玩不起来。
刚好有这次征文活动,把我接触到飞思卡尔芯片以来,以一个飞思卡尔新手来说说芯片的使用感觉说一下,顺便说说我的入门经历和使用心得。
参加活动时候特意搜了下,基于MDK的环境的例程相对较少。心里有点悬。后来开发板收到了,看了下配套的书,大概翻了下基本资源的简介,心里有底了。
GPIO的配置,串口,SPI的配置,相对比较简单。跟STM32一大串关键字定义的库函数相比,可以理解为飞思卡尔的芯片的寄存器配置,就是稍微复杂的51单片机。
赶快到网上找例程,找DEMO程序。找到了一个点灯的。挡下来,看了下MKL25Z4.h 的寄存器定义,关键字定义。对照书上发现不怎么对劲,举个例子 MDK例程 是 SIM->SCGC4 ,结果书上配套是 SIM_SCGC4。。。等等一些不太一样。
好在字符都是一样的。就是要动手修改。
慢慢的,熟悉了寄存器操作和 MKL25Z4.h 的定义 一切都变得简单了。
先写到这里,后边楼层会 谈谈,基于MDK的 飞思卡尔 KL25 的一些 模块的使用。 期待楼主更多心得分享哦! 下边谈谈GPIO的基本控制。
1:SIM_SCGC5 寄存器。使能GPIO的时钟的。下边是mkl25z.h关于SIM_SCGC5的基本定义。
/* SCGC5 Bit Fields */
#define SIM_SCGC5_LPTMR_MASK 0x1u
#define SIM_SCGC5_LPTMR_SHIFT 0
#define SIM_SCGC5_TSI_MASK 0x20u
#define SIM_SCGC5_TSI_SHIFT 5
#define SIM_SCGC5_PORTA_MASK 0x200u
#define SIM_SCGC5_PORTA_SHIFT 9
#define SIM_SCGC5_PORTB_MASK 0x400u
#define SIM_SCGC5_PORTB_SHIFT 10
#define SIM_SCGC5_PORTC_MASK 0x800u
#define SIM_SCGC5_PORTC_SHIFT 11
#define SIM_SCGC5_PORTD_MASK 0x1000u
#define SIM_SCGC5_PORTD_SHIFT 12
#define SIM_SCGC5_PORTE_MASK 0x2000u
#define SIM_SCGC5_PORTE_SHIFT 13
在GPIO口初始化配置前先要是能 相应的 位。
SIM->SCGC5 |= (SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK ); /* Enable Clock to Port A - E */
上边这个例子就是 使能 PORTA PORTB PORTC PORTD PORTE的时钟。
其次就是配置引脚功能复用配置。
下边这个是 mkl25z.h 里对于 GPIO的结构体定义。
typedef struct {
__IO uint32_t PCR; /**< Pin Control Register n, array offset: 0x0, array step: 0x4 */
__Ouint32_t GPCLR; /**< Global Pin Control Low Register, offset: 0x80 */
__Ouint32_t GPCHR; /**< Global Pin Control High Register, offset: 0x84 */
uint8_t RESERVED_0;
__IO uint32_t ISFR; /**< Interrupt Status Flag Register, offset: 0xA0 */
} PORT_Type;
下边是 每一组IO的 地址定义
/** Peripheral PORTA base address */
#define PORTA_BASE (0x40049000u)
/** Peripheral PORTA base pointer */
#define PORTA ((PORT_Type *)PORTA_BASE)
/** Peripheral PORTB base address */
#define PORTB_BASE (0x4004A000u)
/** Peripheral PORTB base pointer */
#define PORTB ((PORT_Type *)PORTB_BASE)
/** Peripheral PORTC base address */
#define PORTC_BASE (0x4004B000u)
/** Peripheral PORTC base pointer */
#define PORTC ((PORT_Type *)PORTC_BASE)
/** Peripheral PORTD base address */
#define PORTD_BASE (0x4004C000u)
/** Peripheral PORTD base pointer */
#define PORTD ((PORT_Type *)PORTD_BASE)
/** Peripheral PORTE base address */
#define PORTE_BASE (0x4004D000u)
/** Peripheral PORTE base pointer */
#define PORTE ((PORT_Type *)PORTE_BASE)
/** Array initializer of PORT peripheral base pointers */
#define PORT_BASES { PORTA, PORTB, PORTC, PORTD, PORTE }
例如想设置PORTB的第1位(PB0,PB1)为输出可以这样写。
1:首先选择 引脚复用为基本GPIO。
PORTB->PCR= PORT_PCR_MUX(1);
PORTB->PCR= PORT_PCR_MUX(1);
PORTB上边已经又定义了。PCR 每一组(PORTA,PORTB......)的每一个管脚都有一个 PCR寄存器。
为什么要 PORT_PCR_MUX(1)
下边是 mkl25z.h 对于 PORT_PCR_MUX 的定义。
#define PORT_PCR_MUX(x) (((uint32_t)(((uint32_t)(x))<<PORT_PCR_MUX_SHIFT))&PORT_PCR_MUX_MASK)
参考手册
PORTB 的第0个管脚 ALT0 : ADC0SE8 ALT1: PTB0 ALT2:IIC0_SCL ALT3 :TPM1_CH0 ;
PORTB 的第1个管脚 ALT0 : ADC0SE9 ALT1: PTB1 ALT2:IIC0_SDA ALT3 :TPM1_CH1 ;
想使用什么功能,就PORT_PCR_MUX(n)。
我们要的是输出,属于PTBn功能。我们就 PORT_PCR_MUX(1);
2:下边开始配置引脚功能的输入输出。
这是 mkl25z.h 对于引脚控制配置的结构体定义。
/** FGPIO - Register Layout Typedef */
typedef struct {
__IO uint32_t PDOR; /**< IO数据输出寄存器 */
__Ouint32_t PSOR; /**< IO数据置一寄存器 */
__Ouint32_t PCOR; /**< IO数据清零寄存器 */
__Ouint32_t PTOR; /**< IO数据翻转寄存器 */
__Iuint32_t PDIR; /**< IO数据输入寄存器 */
__IO uint32_t PDDR; /**< IO引脚方向寄存器 */
} FGPIO_Type;
FPTB->PDDR |= (1UL<<0)|(1UL<<1); /* enable PTB0/1 as Output */
PDDR 是引脚输入输出控制寄存器 ,是一个32位宽的寄存器。 从低位开始 每一个bit 对应一个 引脚。 (bit0 -》PTB0 ,bit1-》PTB1..........)如果某BIT 为1 则相应的 PTBn 为输出。 为0 则为输入。芯片复位默认的是 0,即为输入。
现在引脚功能已经基本配置玩了。
3:在电路中我们可能还要设置 默认输出电平。PODR 为输出电平控制,芯片复位上电是0所以输出是0,如果我们想输出是1则可以按下边的方法操作。
FPTB->PODR |= (1UL<<0)|(1UL<<1);
设置 PTB0,PTB1 输出 高电平。
至此 我们引脚的功能配置就完成了。
下边再说说引脚的基本控制
PSOR 某1BIT为1 则输出引脚 会输出1.PDOR的相应BIT也会为1.
PCOR某1BIT为1 则输出引脚 会输出0.PDOR的相应BIT也会为0.
PTOR 某1BIT为1 则输出引脚 会翻转电平,即原来为0,现在为1。
下边是我们对于输出高低电平的定义
#define PTB0L FPTB->PCOR |= 1UL << 0;
#define PTB0H FPTB->PSOR |= 1UL << 0;
#define PTB1L FPTB->PCOR |= 1UL << 1;
#define PTB1H FPTB->PSOR |= 1UL << 1;
.如果将引脚配置为输入的话,PD0R 的值决定了是否上拉。
读输入口状态的话
#define PTB0INFPTB->PDIR &(1UL<<0)
#define PTB1INFPTB->PDIR &(1UL<<1)
PDOR; /**< IO数据输出寄存器 */
PSOR; /**< IO数据置一寄存器 */
PCOR; /**< IO数据清零寄存器 */
PTOR; /**< IO数据翻转寄存器 */
PDIR; /**< IO数据输入寄存器 */
PDDR; /**< IO引脚方向寄存器 */
有人要问了,这么多寄存器不麻烦么。
这对于位操作相当方便啊。
输出0 直接对PCOR 相应的BIT操作,不用考虑其他的BIT.
输出1 直接对PSOR 相应的BIT操作,不用考虑其他的BIT.
要想翻转某个位 直接对 PTOR相应的BIT 操作。省的读取在判断再写入。
直接操作寄存器确实麻烦,还好有周立功的例程可以参考 duhui75 发表于 2015-3-5 12:22
直接操作寄存器确实麻烦,还好有周立功的例程可以参考
我比较喜欢对寄存器操作,最起码程序大概是什么样。自己心里有数。。呵呵 XUEPENGBIN 发表于 2015-3-5 16:30
我比较喜欢对寄存器操作,最起码程序大概是什么样。自己心里有数。。呵呵 ...
那得操作到猴年马月啊 工作是讲究效率的 XUEPENGBIN 发表于 2015-3-2 18:02
下边谈谈GPIO的基本控制。
1:SIM_SCGC5 寄存器。使能GPIO的时钟的。下边是mkl25z.h关于SIM_SCGC5的基本定 ...
楼主你的这个帖子很好,虽然我看了一遍没怎么明白,我再多看几遍。32位和原来16位的真是相差太大了。我那个ke02的板子我就新建了个工程,结果我倒ke02头文件里面看是什么也看不懂。你这个帖子说的不错!谢谢分享! duhui75 发表于 2015-3-6 10:44
那得操作到猴年马月啊 工作是讲究效率的
没有啊。寄存器也没多少。都是比较简单的拉。。个人感觉,相比STM32来说。飞思卡尔的寄存器配置很简单。下一篇,写写,USART。。。最近有点忙。。 jiang887786 发表于 2015-3-6 11:03
楼主你的这个帖子很好,虽然我看了一遍没怎么明白,我再多看几遍。32位和原来16位的真是相差太大了。我那 ...
可能我的表达不是很清楚。给个完整的例子你就明白了。
void LCD_Gpio_Config(void)
{
SIM->SCGC5 |=(SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK ); //使能时钟A------E
//配置PD0---PD7 为普通IO;
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
PORTD->PCR= PORT_PCR_MUX(1);
//设置输出高电平
FPTD->PDOR = 0XFF;
//设置管脚为输出
FPTD->PDDR = 0XFF;
}
管脚初始化就好了。
下边是预定义
#define CSRC522LFPTD->PCOR |= 1UL << 0
#define CSRC522HFPTD->PSOR |= 1UL << 0
int main (void)
{
CSRC522L;
.
.
.
CSRC522H;
.
.
}
飞思卡尔串口的使用。。。
#define UART_PARAM_DEBUG 0 /* 是否进行参数检查 */
#define UART_PARAM_PORTNUM(x) (x > 3? 1 : 0) /* 端口号检查 */
#define UART_PARAM_MODE(x) (x > 3? 1 : 0) /* 模式检查 */
#define UART_PARAM_LENGTH(x) ((x == 8)||(x == 9)||(x == 9) ? 1 : 0) /* 数据长度检查 */
#define UART_PARAM_STOP(x) ((x == 1)||(x == 2)? 1 : 0) /* 停止位检查 */
#define UART_PARAM_PARITY(x) ((x == 0)||(x == 1)? 1 : 0) /* 校验位检查 */
#define UART_PARAM_LOGIC(x) ((x == 0)||(x == 1)? 1 : 0) /* 逻辑位检查 */
#define UART_CHECK_PARAM(x) (x? (while (1)):) /* 错误处理 */
#define UART0_DEFAULT_OPEN 1 /* 是否默认打开传输 */
#define UART0_IRQ_ENABLE 1 /* 串口中断允许 */
#define UART1_DEFAULT_OPEN 1 /* 是否默认打开传输 */
#define UART1_IRQ_ENABLE 1 /* 串口中断允许 */
串口1和串口2 是基本相同,把串口1分析一下。
void uart1Init( uint32_tulBaudRate,
uint8_t ucParityEnable,
uint8_t ucParityType,
uint8_t ucDataLength,
uint8_t ucStopBit)
{
register uint16_t usBaudRate= 0;
#if UART_PARAM_DEBUG
UART_CHECK_PARAM(UART_PARAM_LENGTH(ucDataLength));
UART_CHECK_PARAM(UART_PARAM_STOP(ucStopBit));
UART_CHECK_PARAM(UART_PARAM_LOGIC(ucParityEnable));
UART_CHECK_PARAM(UART_PARAM_PARITY(ucParityType));
#endif
SIM->SCGC4 |= SIM_SCGC4_UART1_MASK; /* 允许外设时钟 */
uart1TranControl(UART_TX_DISABLE, UART_RX_DISABLE); /* 首先禁止通信 */
// 配置串口使用的引脚;
PORTC->PCR = PORT_PCR_MUX(0x3); /* UART0_TXD */
PORTC->PCR = PORT_PCR_MUX(0x3); /* UART0_RXD */
// 有485的话,配置485的控制引脚
PORTC->PCR = PORT_PCR_MUX(0x1);
FPTC->PDOR |=(1UL<<2);
FPTC->PDDR |=(1UL<<2);
//485控制引脚配置结束
UART1->C1&= ~(UART_C1_M_MASK |UART_C1_PT_MASK |UART_C1_PE_MASK);/* 数据长度 */
/* 校验位类型 */
/* 校验位 */
UART1->C1|= ((ucDataLength - 8UL) << UART_C1_M_SHIFT)|
(ucParityEnable << UART_C1_PE_SHIFT)|
(ucParityType << UART_C1_PT_SHIFT);
usBaudRate = SystemBusClock/(ulBaudRate * 16);
UART1->BDH =(usBaudRate & 0x1F00) >> 8; /* 波特率 */
UART1->BDL =(uint8_t)(usBaudRate & UART_BDL_SBR_MASK);
UART1->BDH&= ~UART_BDH_SBNS_MASK; /* 停止位 */
UART1->BDH|=(ucStopBit - 1) << UART_BDH_SBNS_SHIFT;
UART1->C2 &= ~(UART_C2_TIE_MASK|
UART_C2_TCIE_MASK |
UART_C2_RIE_MASK|
UART_C2_ILIE_MASK); /* 清除中断设置 */
while((UART1->S1& UART_S1_RDRF_MASK) && (UART1->D)); /* 清接收缓冲区 */
#if UART1_DEFAULT_OPEN
uart1TranControl(UART_TX_ENABLE, UART_RX_ENABLE); /* 配置完成允许通信 */
#endif
#if UART1_IRQ_ENABLE
#if UART1_SEND_IRQ
UART1->C2|= UART_C2_TCIE_MASK;
#endif
#if UART1_RECEIVE_IRQ
UART1->C2 |= UART_C2_RIE_MASK;
#endif
NVIC_EnableIRQ(UART1_IRQn);
#endif
}
//串口传输控制。
void uart1TranControl(uint8_t ucTxEnable,
uint8_t ucRxEnable)
{
#if UART_PARAM_DEBUG
UART_CHECK_PARAM(UART_PARAM_LOGIC(ucTxEnable));
UART_CHECK_PARAM(UART_PARAM_LOGIC(ucRxEnable));
#endif
UART1->C2&= ~(UART_C2_TE_MASK | UART_C2_RE_MASK);
UART1->C2|= (ucTxEnable << UART_C2_TE_SHIFT)| (ucRxEnable << UART_C2_RE_SHIFT);
}
如果使能中断了下边是串口中断服务程序
由于串口的中断包括发送和接收中断。在此根据标记位来区分;
UART0_S1_RDRF_MASK串口接收到有效数据
UART0_S1_TDRE_MASK串口发送寄存器空
UART0_C2_TIE_MASK 串口发送中断使能位
void UART0_IRQHandler(void)
{
__disable_irq();
#if UART0_SEND_IRQ
/* 发送中断处理程序 */
while((UART0->S1& UART0_S1_TDRE_MASK)&&(UART0->C2 & UART0_C2_TIE_MASK))
{
(用户的发送中断处理函数);
}
#endif
#if UART0_RECEIVE_IRQ
while(UART0->S1& UART0_S1_RDRF_MASK)
{
(用户的接收中断处理函数)
}
#endif
__enable_irq();
}
在用户写发送程序时 根据发送的数据缓冲来判断发送数据是否是最后一个字节,是的话,要及时清除掉 UART0_C2_TIE_MASK 这样就可以自由控制串口串口发送中断。
页:
[1]