打印
[FPGA]

MCU驱动使用(二)

[复制链接]
52|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
HIZYUAN|  楼主 | 2025-4-30 11:24 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
五、Base Timer的使用:
六、gpTimer的使用:
    1、用于简单定时
    2、用于pwm输出
    3、输出反向PWM(带死区)
    4、输出任意波形
    5、用于输入捕获
七、Uart的使用
八、IIC的使用
九、CAN的使用


五、Base Timer的使用:
AG32中包含2个Base Timer:分别对应TIMER0和TIMER1。
这两个timer中,每个又有两组寄存器,每组寄存器可以单独产生定时。
所以,真正可用的普通定时器有4个:TIMER0-0、TIMER0-1、TIMER1-0、TIMER1-1。
4个定时器均可独立设置。
普通定时器特点:

定时器支持16位和32位的设置,
支持3种类型分频(1分频,16分频,256分频),
支持单次定时和循环定时。

驱动API函数命名中的1和2,分别对应第一组和第二组寄存器。也就是说,一个Timer可以用于2个独立计时器。
如,TIM_Init1设置的是第一组寄存器,TIM_Init2设置的是第二组寄存器。

举例:
用Timer1的group2产生1s的循环定时:


中断函数TIMER1_isr在SDK中已经默认指定。
说明:
设置函数:TIM_Init1 <-> TIM_SetLoad1/TIM_SetSize1/TIM_SetMode1/...
中断函数:TIMER0_isr/TIMER1_isr
函数说明:
void TIM_Init1(TIMER_TypeDef *tim, uint32_t timeInUs, TIMER_ModeTypeDef mode)
作用:启动Timer0或Timer1的第一个定时器(TIM_Init2则启动第二个定时器)。
参数:tim:TIMER0 or TIMER1
  timeInUs:多少us触发定时
  mode:TIMER_MODE_PERIODIC:循环触发  TIMER_CTRL_ONESHOT:只触发一次
举例:
  TIM_Init1(TIMER0, 500000, TIMER_MODE_PERIODIC);  
表示启动TIMER0的第一个定时器,500ms触发一次定时中断,循环触发。
除了直接调用 TIM_Init1来启动一个定时外,也可以调用各个子函数来启动。
如,
  TIM_Init2(TIMER0, 500000, TIMER_MODE_PERIODIC)
功能等价于:

TIM_SetLoad2(TIMER0, SYS_GetPclkFreq() / 1000000 * 500000);
TIM_SetSize2(TIMER0, TIMER_SIZE_32);
TIM_SetMode2(TIMER0, TIMER_MODE_PERIODIC);
TIM_SetPrescaler2(TIMER0, TIMER_PRESCALE_1);
TIM_EnableInt2(TIMER0);
TIM_EnableTimer2(TIMER0);

以上几个函数中,
TIM_SetPrescaler2 是设置分频,
  三个参数可选:TIMER_PRESCALE_1/TIMER_PRESCALE_16/TIMER_PRESCALE_256
  分别表示分频数:1分频,16分频,256分频;
TIM_SetSize2 设置计时器位宽,
  两个参数可选:TIMER_SIZE_32/TIMER_SIZE_16
  表示计数器的load的位宽是32位还是16位。
TIM_SetLoad2 设置触发时间(以tick为单位)
  如果定时单位为ms,则需要将tick转为ms:SYS_GetPclkFreq()/1000000*ms
中断函数:void TIMER0_isr()
函数说明:该函数为TIMER0的中断函数;
在该函数中需要先查询是第一个还是第二个定时器,然后再清中断。
该中断函数已默认关联,不需要程序中来手工设置。
完整代码样例请参考example下example_timer.c。

六、gpTimer的使用:
General Purpose Timer,即通用计时器。相当于ST中的Advanced Timer.
AG32中包含5个通用计时器(GpTimer),
代码中分别对应:GPTIMER0、GPTIMER1、GPTIMER2...
通用定时器可以实现更多功能,包括:计时、生成pwm、生成任意波形、输入捕获。
5个定时器均可独立设置。
每个定时器支持4个独立通道(channel):
-- 输入捕获
-- PWM输出(边缘或中间对齐模式)
-- 单脉冲输出
主要函数:GPTIMER_Init / GPTIMER_OC_Init.

1、用于简单定时
用于简单定时,只需要关注一个函数:GPTIMER_Init,
设置好参数后,启动计时即可。
举例:
用gpTimer1产生2秒一次的定时。


这里使用到的中断函数GPTIMER1_isr,已被SDK自动设置。

2、用于pwm输出
用于pwm输出时,要设置两个函数:GPTIMER_Init 和 GPTIMER_OC_Init。
GPTIMER_Init中设置多长时间触发一次timer;
GPTIMER_OC_Init中指定pwm输出通道及设置pwm的占空比;
举例:
用gpTimer4在通道0上产生pwm输出。


除了上述的代码控制外,还需要在ve中添加映射关系:


这样的情况下,pwm才会输出到管脚上。
典型案例:呼吸灯(用timer+timerPWM来控制led灯逐渐变量逐渐变暗)

3、输出反向PWM(带死区)
样例程序,请参考网盘下“其他文档\驱动样例补充\example_gptimer_pwm_N.c”

4、输出任意波形
如果要输出的不是pwm的规则波形,而是不规则波形(比如正弦波),则可借助于DMA方式来模拟实现。
思路:事先在数组中定义好数据序列,然后通过dma每次搬运,作用到输出。
这部分功能,参考例程函数:TestGpTimerDma
这种方式也同样需要管脚映射。

5、用于输入捕获
用于输入捕获时,要设置两个函数:GPTIMER_Init 和 GPTIMER_IC_Init。
样例程序,请参考网盘下“其他文档\驱动样例补充\example_gptimer_capture.c”

七、Uart的使用
AG32可用的UART有5个,分别对应UART0、UART1、UART2、UART3、UART4。几个Uart的功能和用法是完全相同的。
样例工程中,UART0被做为输出log的串口。其他几个UART可被用户直接使用。
串口使用较为简单,这里讲述下几个重要函数:
初始化函数:
void UART_Init(UART_TypeDef *uart, UART_BaudRateTypeDef baudrate, UART_LCR_DataBitsTypeDef databits,
        UART_LCR_StopBitsTypeDef stopbits, UART_LCR_ParityTypeDef parity, UART_LCR_FifoTypeDef fifo)
参数说明:
  • Uart:UART0、UART1、UART2、UART3 or UART4
  • Baudrate:波特率,如 115200
  • Databits/stopbits/parity:
  • Fifo:是否开启16字节的fifo缓冲
收发函数:
  • UART_Send(UART_TypeDef *uart, const unsigned char *p, unsigned int num)
  • UART_Receive(UART_TypeDef *uart, unsigned char *p, unsigned int num, unsigned int timeout)
  • 收函数的timeout,是如果收不满num个字符,就等待多少个tick。可以为0。

样例1、实现Uart1的简单收发
  • 增加ve对uart1的管脚配置:




代码中实现如下:




样例2、使用接收中断(8字节)来收取数据
这种方式,是使用了FIFO的收中断;(可配置 收到2/4/8/12/16 bytes时触发中断)
代码部分可参考以下方式(新增的红框代码):


中断函数UART1_isr在SDK中已经默认关联,不用手动设置。
在中断函数中,要判别中断来源再继续操作。
上例中,收FIFO因为设置为16字节,半数触发时,收到8个字节就会触发中断。

样例3、使用接收中断(1字节)来收取数据
相比样例2,如果想要来1个字节就触发一次中断,可以使用这里的方式。
代码方面:在UART_Init中设置参数为UART_LCR_FIFO_1,并且不用再调UART_SetRxIntFifoLevel函数。


样例4、使用DMA收发
如果要启用DMA功能,参考sdk中自带的样例。
需要增加3个函数:
DMAC_Init:启动dma
UART_SetDmaMode:设置只要收/发dma,或收发都要dma
DMAC_Config:设置dma的详细参数。

如果收发都要dma,则需要调用2次DMAC_Config来分别设置。
函数DMAC_Config的参数说明:
void DMAC_Config(
    DMAC_ChannelNumTypeDef channel, //DMA通道
    uint32_t srcAddr, //DMA数据源地址
    uint32_t dstAddr, //DMA数据目标地址
    DMAC_AddrIncTypeDef srcIncr, //传输后源地址是否自增
    DMAC_AddrIncTypeDef dstIncr, //传输后目标地址是否自增
    DMAC_WidthTypeDef srcWidth,  //源地址传输数据的字节宽度(可选8/16/32)
    DMAC_WidthTypeDef dstWidth,  //目标地址传输数据的字节宽度(可选8/16/32)
    DMAC_BurstTypeDef srcBurst,  //源地址一次传输多少
    DMAC_BurstTypeDef dstBurst,
    uint32_t transferSize,   //传输多少次
    DMAC_FlowControlTypeDef transferType, //传输方向类型(8种)
    uint32_t srcPeripheral,      //源地址的外设类型
    uint32_t dstPeripheral      //目标地址的外设类型
)
比如,设置收DMA,会设置参数如:
DMAC_Config(DMAC_CHANNEL1,
    (uint32_t)&UART3->DR, //串口数据寄存器
    (uint32_t)rxbuf,     //收缓冲buff
    DMAC_ADDR_INCR_OFF,    //源地址不自增
    DMAC_ADDR_INCR_ON,     //目标地址自增
    DMAC_WIDTH_8_BIT,      //源数据宽度以8bit为单位
    DMAC_WIDTH_8_BIT,     //目标数据宽度以8bit为单位
    DMAC_BURST_1,
    DMAC_BURST_1,
    0,                    //传输多少次,如果是0则无限制
    DMAC_PERIPHERAL_TO_MEM_PERIPHERAL_CTRL, //外设到内存的方向
    UART3_RX_DMA_REQ,      //源数据外设类型
    0 );                  //目标数据外设类型
设置发的DMA,会设置参数如:
DMAC_Config(DMAC_CHANNEL0,
    (uint32_t)txbuf,      //发缓冲
    (uint32_t)&UART3->DR, //串口数据寄存器
    DMAC_ADDR_INCR_ON,     //发缓冲自增
    DMAC_ADDR_INCR_OFF,    //寄存器不自增
    DMAC_WIDTH_8_BIT,     //源数据宽度以8bit为单位
    DMAC_WIDTH_8_BIT,     //目标数据宽度以8bit为单位
    DMAC_BURST_1,
    DMAC_BURST_1,
    dma_count,            //要传输的数据量
    DMAC_MEM_TO_PERIPHERAL_DMA_CTRL, //内存到外设的方向
    0,                //源数据外设类型
    tx_dma_req);           //目标数据外设类型
以上完整代码样例请参考example部分。

更多样例,请参考网盘
1).dma中断:“其他文档\驱动样例补充\example_uart_dmaIrq.c”
2).闲时中断:“其他文档\驱动样例补充\example_uart_rcvIqr.c”


八、IIC的使用
AG32支持两路I2C,分别对应:I2C0、I2C1;
I2C是一种简单的双向两线制总线协议,半双工,支持多主从模式。I2C最大的特点之一就是有完善的应答机制。
MCU端是I2C的主端。
样例程序参考example_i2c.c
在使用I2C时的流程:
1. Ve中先配置对应的引脚:


2. 代码中时钟使能、中断使能、设置频率;


3. 使能I2C;


4. 收发数据;
   IIC的收过程和发过程,都有对应的应答流程,启动->收/发->结束。
   使用中,收发函数会被完整的封装。
   请参考例程函数(函数流程可参考,封装请自行调整):
   bool I2cReadPROM(uint8_t *mem, bool verify)
   bool I2cWritePROM(uint8_t *mem)

5. 关闭I2C:




另外,例程中还使用到了中断函数。当I2C准备好时,会触发该中断。
注意,IIC例程需要接入设备才能测通,否则在I2C_WaitForTransfer函数中会因为等不到Ack而卡住。
.
九、CAN的使用
AG32支持1路CAN,对应:CAN0
样例程序参考example_can.c
在使用CAN时的流程:
  • ve中先配置对应的引脚:





2. 代码中使能时钟、开中断:




3. 配置参数(参数较多)并开启can、开启收中断:




4. 发送数据:




5. 在中断函数中接收数据:




使用时,请参考样例修改。



联系海振远科技
电话:0755-2780 9180;  15323895320;
Lucy@hizyuan.com


12.png (5.57 KB )

12.png

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

21

主题

28

帖子

2

粉丝