Microchip 系列:SAM D 系列 (基于 ARM Cortex-M0+)_5.SAM D系列外设功能详解

5.1 通用输入输出 (GPIO) 功能详解

Microchip 系列:SAM D 系列 (基于 ARM Cortex-M0+)_5.SAM D系列外设功能详解_第1张图片

5.1.1 GPIO 基本概念

通用输入输出 (GPIO) 是单片机中非常基础且重要的外设功能之一。GPIO 通常用于控制外部设备或从外部设备读取数据。在 SAM D 系列中,GPIO 通过特定的寄存器和配置来实现。每个 GPIO 引脚都可以配置为输入或输出模式,并且可以设置不同的中断和触发条件。

5.1.2 GPIO 寄存器和配置

SAM D 系列的 GPIO 控制主要通过以下寄存器实现:

  • DIRSETDIRCLR:用于设置和清除引脚的方向(输入或输出)。

  • OUTSETOUTCLR:用于设置和清除输出引脚的电平。

  • IN:用于读取输入引脚的电平。

  • PINCFG:用于配置引脚的其他特性,如上拉/下拉电阻、驱动能力等。

5.1.2.1 配置 GPIO 引脚

配置 GPIO 引脚的基本步骤如下:

  1. 选择端口:SAM D 系列通常有多个 GPIO 端口,例如 PORTAPORTB 等。

  2. 设置方向:使用 DIRSETDIRCLR 寄存器来设置引脚的方向。

  3. 配置引脚:使用 PINCFG 寄存器来配置引脚的其他特性。

  4. 读写引脚:使用 INOUTSET/OUTCLR 寄存器来读取和设置引脚的电平。

5.1.2.2 示例代码:配置和使用 GPIO 引脚

以下是一个示例代码,展示如何配置和使用 GPIO 引脚。我们将配置 PORTA 的第 0 引脚为输出模式,并设置其电平为高。


#include "sam.h"



void configure_gpio_output(void) {

    // 选择端口 A

    PORTGroup *porta = PORT->Group[0];



    // 设置引脚 0 为输出模式

    porta->DIRSET.reg = PORT_DIRSET_PIN0;



    // 配置引脚 0

    porta->PINCFG[0].reg |= PORT_PINCFG_DRVSTR_Msk; // 设置驱动强度

    porta->PINCFG[0].reg &= ~PORT_PINCFG_PULLEN_Msk; // 禁用上拉/下拉电阻



    // 设置引脚 0 的电平为高

    porta->OUTSET.reg = PORT_OUTSET_PIN0;

}



int main(void) {

    // 配置 GPIO 引脚

    configure_gpio_output();



    // 主循环

    while (1) {

        // 保持运行

    }

}

5.1.3 GPIO 中断配置

GPIO 中断允许单片机在引脚状态发生变化时触发中断处理程序。通过配置中断,可以实现更高效的事件响应。

5.1.3.1 中断配置步骤
  1. 选择中断源:确定哪个引脚需要配置中断。

  2. 配置中断类型:选择中断触发类型,如上升沿、下降沿或双边沿。

  3. 启用中断:在相应的寄存器中启用中断。

  4. 编写中断处理函数:定义中断处理函数,并在初始化时注册该函数。

5.1.3.2 示例代码:配置 GPIO 中断

以下是一个示例代码,展示如何配置 PORTA 的第 1 引脚为中断输入,并在引脚状态变化时触发中断处理函数。


#include "sam.h"

#include "system_samd21.h"

#include "port.h"



// 中断处理函数

void PORTA_Handler(void) {

    // 清除中断标志

    PORTGroup *porta = PORT->Group[0];

    porta->INTFLAG.reg = PORT_INTFLAG_PIN1;



    // 处理中断

    if (porta->IN.reg & PORT_PIN1) {

        // 引脚电平为高

        PORT->Group[0].OUTSET.reg = PORT_OUTSET_PIN0; // 设置 Pin0 为高

    } else {

        // 引脚电平为低

        PORT->Group[0].OUTCLR.reg = PORT_OUTCLR_PIN0; // 设置 Pin0 为低

    }

}



void configure_gpio_interrupt(void) {

    // 选择端口 A

    PORTGroup *porta = PORT->Group[0];



    // 设置引脚 1 为输入模式

    porta->DIRCLR.reg = PORT_DIRCLR_PIN1;



    // 配置引脚 1

    porta->PINCFG[1].reg |= PORT_PINCFG_PULLEN_Msk; // 启用上拉电阻

    porta->PINCFG[1].reg &= ~PORT_PINCFG_INEN_Msk; // 启用输入



    // 配置中断类型

    porta->INTENSET.reg = PORT_INTENSET_PIN1; // 启用 Pin1 中断

    porta->EVCTRL.reg |= PORT_EVCTRL_PIN1; // 选择中断触发类型(例如双边沿)



    // 注册中断处理函数

    NVIC_SetPriority(PORTA_IRQn, 0); // 设置中断优先级

    NVIC_EnableIRQ(PORTA_IRQn); // 启用中断

}



int main(void) {

    // 配置 GPIO 引脚

    configure_gpio_output();

    configure_gpio_interrupt();



    // 主循环

    while (1) {

        // 保持运行

    }

}

5.1.4 GPIO 多功能复用

SAM D 系列的 GPIO 引脚通常可以复用为多种功能,例如定时器、ADC 输入、PWM 输出等。通过配置引脚的多功能复用,可以实现更灵活的外设控制。

5.1.4.1 多功能复用配置步骤
  1. 选择复用功能:确定引脚需要复用的功能。

  2. 配置引脚功能:使用 PINCFG 寄存器中的 PMUX 字段来选择复用功能。

  3. 启用复用功能:在相应的外设寄存器中启用复用功能。

5.1.4.2 示例代码:配置 GPIO 引脚为 ADC 输入

以下是一个示例代码,展示如何配置 PORTA 的第 2 引脚为 ADC 输入。


#include "sam.h"

#include "system_samd21.h"

#include "adc.h"



void configure_gpio_adc_input(void) {

    // 选择端口 A

    PORTGroup *porta = PORT->Group[0];



    // 设置引脚 2 为输入模式

    porta->DIRCLR.reg = PORT_DIRCLR_PIN2;



    // 配置引脚 2

    porta->PINCFG[2].reg |= PORT_PINCFG_PULLEN_Msk; // 启用上拉电阻

    porta->PINCFG[2].reg |= PORT_PINCFG_INEN_Msk; // 启用输入



    // 配置多功能复用

    porta->PMUX[1].reg = PORT_PMUX_PMUXE_A; // 选择 ADC 功能

    porta->PMUX[1].reg |= PORT_PMUX_PMUXO_A; // 选择 ADC 功能



    // 启用 ADC 功能

    ADC *adc = ADC0;

    adc->CTRLA.reg = ADC_CTRLA_SWRST; // 复位 ADC

    while (adc->CTRLA.reg & ADC_CTRLA_SWRST) {

        // 等待复位完成

    }



    adc->CTRLA.reg = ADC_CTRLA_ENABLE; // 启用 ADC

    adc->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV32; // 设置预分频器

    adc->REFCTRL.reg = ADC_REFCTRL_REFSEL_VREFPU; // 选择参考电压

    adc->EVTCTRL.reg = 0; // 配置事件控制

    adc->INPUTCTRL.reg = ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_PIN2; // 选择输入通道

    adc->AVGCTRL.reg = ADC_AVGCTRL_SAMPNUM_1 | ADC_AVGCTRL_ADJRES_12; // 设置采样次数和分辨率

    adc->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(1); // 设置采样时间

    adc->CTRLA.reg |= ADC_CTRLA_ENABLE; // 重新启用 ADC

}



int main(void) {

    // 配置 GPIO 引脚

    configure_gpio_adc_input();



    // 主循环

    while (1) {

        // 读取 ADC 值

        ADC *adc = ADC0;

        adc->SWTRIG.reg = ADC_SWTRIG_START; // 触发一次转换

        while (!(adc->INTFLAG.reg & ADC_INTFLAG_RESRDY)) {

            // 等待转换完成

        }



        uint16_t adc_value = adc->RESULT.reg & ADC_RESULT_RESULT_Msk; // 读取结果

        // 处理 ADC 值

    }

}

5.2 串行通信接口 (SERCOM) 功能详解

5.2.1 SERCOM 基本概念

串行通信接口 (SERCOM) 是 SAM D 系列中用于实现多种串行通信协议的外设。SERCOM 可以配置为 SPI、UART、I2C 和 I2S 等多种通信模式。每个 SERCOM 模块通常包含多个通信通道,可以根据需要灵活配置。SERCOM 的多功能性和灵活性使得它在嵌入式系统中非常有用,可以轻松实现与外部设备的通信。

5.2.2 SERCOM 寄存器和配置

SERCOM 的配置主要通过以下寄存器实现:

  • CTRLA:用于启用和复位 SERCOM 模块。

  • CTRLB:用于配置通信模式和数据格式。

  • BAUD:用于设置波特率。

  • INTENSETINTENCLR:用于设置和清除中断使能。

  • STATUS:用于读取通信状态。

  • DATA:用于发送和接收数据。

5.2.2.1 配置 SERCOM 模块

配置 SERCOM 模块的基本步骤如下:

  1. 选择通信模式:确定使用哪种通信协议(SPI、UART、I2C、I2S)。

  2. 设置波特率:对于 UART 和 SPI,需要设置波特率。

  3. 配置数据格式:设置数据位、停止位、校验位等。

  4. 启用通信:在 CTRLA 寄存器中启用 SERCOM 模块。

5.2.2.2 示例代码:配置 UART 通信

以下是一个示例代码,展示如何配置 SERCOM 模块为 UART 通信模式,并实现简单的串行通信。


#include "sam.h"

#include "system_samd21.h"

#include "sercom.h"

#include "timer.h"



void configure_uart(void) {

    // 选择 SERCOM 模块

    SERCOM *sercom = SERCOM0;



    // 选择 UART 模式

    sercom->USART.CTRLA.reg = SERCOM_USART_CTRLA_SWRST; // 复位 USART

    while (sercom->USART.CTRLA.reg & SERCOM_USART_CTRLA_SWRST) {

        // 等待复位完成

    }



    // 配置波特率

    uint32_t baud_rate = 9600;

    uint32_t baud_rate_divisor = (SystemCoreClock / (16 * baud_rate)) - 1;

    sercom->USART.BAUD.reg = SERCOM_USART_BAUD_BAUD(baud_rate_divisor);



    // 配置数据格式

    sercom->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN | SERCOM_USART_CTRLB_CHSIZE_8BIT;



    // 启用 USART

    sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;

}



void send_uart_data(char *data) {

    SERCOM *sercom = SERCOM0;

    while (*data) {

        while (!(sercom->USART.STATUS.reg & SERCOM_USART_STATUS_DRE)) {

            // 等待数据寄存器为空

        }

        sercom->USART.DATA.reg = *data++;

    }

}



char receive_uart_data(void) {

    SERCOM *sercom = SERCOM0;

    while (!(sercom->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_RXC)) {

        // 等待接收数据

    }

    return sercom->USART.DATA.reg;

}



int main(void) {

    // 配置 UART

    configure_uart();



    // 主循环

    while (1) {

        // 发送数据

        send_uart_data("Hello, UART!\r\n");



        // 接收数据

        char received_data = receive_uart_data();

        // 处理接收到的数据

    }

}

5.2.3 SPI 通信配置

SPI (Serial Peripheral Interface) 是一种同步串行通信协议,常用于连接微控制器和外部设备。SAM D 系列的 SERCOM 模块可以配置为 SPI 模式。

5.2.3.1 配置 SPI 模块

配置 SPI 模块的基本步骤如下:

  1. 选择 SPI 模式:确定 SPI 是主模式还是从模式。

  2. 设置波特率:配置 SPI 的通信速度。

  3. 配置数据格式:设置数据位、时钟极性和相位等。

  4. 启用 SPI:在 CTRLA 寄存器中启用 SPI 模块。

5.2.3.2 示例代码:配置 SPI 主模式

以下是一个示例代码,展示如何配置 SERCOM 模块为 SPI 主模式,并实现简单的 SPI 通信。


#include "sam.h"

#include "system_samd21.h"

#include "sercom.h"

#include "timer.h"



void configure_spi_master(void) {

    // 选择 SERCOM 模块

    SERCOM *sercom = SERCOM0;



    // 选择 SPI 模式

    sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_SWRST; // 复位 SPI

    while (sercom->SPI.CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) {

        // 等待复位完成

    }



    // 配置波特率

    uint32_t baud_rate = 1000000;

    uint32_t baud_rate_divisor = (SystemCoreClock / (2 * baud_rate)) - 1;

    sercom->SPI.BAUD.reg = SERCOM_SPI_BAUD_BAUD(baud_rate_divisor);



    // 配置数据格式

    sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE_8BIT | SERCOM_SPI_CTRLB_CPOL | SERCOM_SPI_CTRLB_CPHA;



    // 配置主模式

    sercom->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(3); // 主模式

    sercom->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;

}



void send_spi_data(uint8_t *data, uint8_t length) {

    SERCOM *sercom = SERCOM0;

    while (length--) {

        while (!(sercom->SPI.INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE)) {

            // 等待数据寄存器为空

        }

        sercom->SPI.DATA.reg = *data++;

    }

}



uint8_t receive_spi_data(uint8_t length) {

    SERCOM *sercom = SERCOM0;

    uint8_t received_data = 0;

    while (length--) {

        while (!(sercom->SPI.INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC)) {

            // 等待接收数据

        }

        received_data = sercom->SPI.DATA.reg;

    }

    return received_data;

}



int main(void) {

    // 配置 SPI

    configure_spi_master();



    // 主循环

    while (1) {

        // 发送数据

        uint8_t tx_data[] = {0x55, 0xAA, 0x66};

        send_spi_data(tx_data, 3);



        // 接收数据

        uint8_t rx_data = receive_spi_data(1);

        // 处理接收到的数据

    }

}

5.2.4 I2C 通信配置

I2C (Inter-Integrated Circuit) 是一种用于连接微控制器和外部设备的同步串行通信协议。SAM D 系列的 SERCOM 模块可以配置为 I2C 模式。

5.2.4.1 配置 I2C 模块

配置 I2C 模块的基本步骤如下:

  1. 选择 I2C 模式:确定 I2C 是主模式还是从模式。

  2. 设置波特率:配置 I2C 的通信速度。

  3. 配置地址:设置 I2C 的设备地址。

  4. 启用 I2C:在 CTRLA 寄存器中启用 I2C 模块。

5.2.4.2 示例代码:配置 I2C 主模式

以下是一个示例代码,展示如何配置 SERCOM 模块为 I2C 主模式,并实现简单的 I2C 通信。


#include "sam.h"

#include "system_samd21.h"

#include "sercom.h"

#include "timer.h"



void configure_i2c_master(void) {

    // 选择 SERCOM 模块

    SERCOM *sercom = SERCOM0;



    // 选择 I2C 模式

    sercom->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; // 复位 I2C

    while (sercom->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST) {

        // 等待复位完成

    }



    // 配置波特率

    uint32_t baud_rate = 100000;

    uint32_t baud_rate_divisor = (SystemCoreClock / (2 * baud_rate)) - 1;

    sercom->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud_rate_divisor);



    // 配置主模式

    sercom->I2CM.CTRLA.reg |= SERCOM_I2CM_CTRLA_MODE(0); // 主模式

    sercom->I2CM.CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE;



    // 配置 I2C 地址

    sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(0x3C) | SERCOM_I2CM_ADDR_HSA; // 设置目标设备地址

}



void send_i2c_data(uint8_t address, uint8_t *data, uint8_t length) {

    SERCOM *sercom = SERCOM0;



    // 发送起始信号

    sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(1); // 发送起始信号

    while (sercom->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) {

        // 等待总线空闲

    }



    // 发送目标地址

    sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(address) | SERCOM_I2CM_ADDR_HSA;

    while (!(sercom->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_AMATCH)) {

        // 等待地址匹配

    }



    // 发送数据

    while (length--) {

        while (!(sercom->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_DRE)) {

            // 等待数据寄存器为空

        }

        sercom->I2CM.DATA.reg = *data++;

    }



    // 发送停止信号

    sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(3); // 发送停止信号

}



uint8_t receive_i2c_data(uint8_t address) {

    SERCOM *sercom = SERCOM0;



    // 发送起始信号

    sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(1); // 发送起始信号

    while (sercom->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) {

        // 等待总线空闲

    }



    // 发送目标地址(读模式)

    sercom->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(address) | SERCOM_I2CM_ADDR_HSA | SERCOM_I2CM_ADDR_READ;

    while (!(sercom->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_AMATCH)) {

        // 等待地址匹配

    }



    // 接收数据

    while (!(sercom->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_RXC)) {

        // 等待接收数据

    }

    uint8_t received_data = sercom->I2CM.DATA.reg;



    // 发送停止信号

    sercom->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(3); // 发送停止信号



    return received_data;

}



int main(void) {

    // 配置 I2C

    configure_i2c_master();



    // 主循环

    while (1) {

        // 发送数据

        uint8_t tx_data[] = {0x01, 0x02, 0x03};

        send_i2c_data(0x3C, tx_data, 3);



        // 接收数据

        uint8_t rx_data = receive_i2c_data(0x3C);

        // 处理接收到的数据

    }

}

5.2.5 总结

通过上述示例代码,我们可以看到 SAM D 系列的 SERCOM 模块在配置和使用不同通信协议时的基本步骤和寄存器配置。无论是 UART、SPI 还是 I2C,配置过程都遵循类似的步骤:选择通信模式、设置波特率、配置数据格式和启用通信。这些配置使得 SAM D 系列微控制器能够灵活地与各种外部设备进行通信,满足不同应用场景的需求。

你可能感兴趣的:(单片机开发,arm开发,单片机,嵌入式硬件)