舒服的WEB服务器框架所需具备的特性 - 模板引擎 * - 数据库连接池 * - RESTFul * - 简洁的路由 * - xml、json、form 等结构与object的快速转换 * - 依赖注入 Golang 的 martini 框架几乎全包含了!

STM32开发入门系列之七:SPI通信

SPI通信是一种高效的同步通信协议,通过4根引脚,实现了数据的高速双向通信。这4根针分工特别简单,简要说明一下:

  • NSS,使能信号,可定义高电平使能或者低电平使能
  • SCK,时钟信号,由主机发起。上升沿之前,主机设置好需要写给从机的值,供从机在高电平的时候读取;下降沿之前,从机设置好需要写给主机的值,供主机在下降沿读取。
  • MISO,主机输入,从机输出引脚。(M:master 主机,S:slaver 从机)
  • MOSI,主机输出,从机输入引脚。

根据STM32的引脚定义手册,我们可以清晰地看到PA4,PA5,PA6,PA7分别对应上面的NSSS,SCK,MISO,MOSI,截图如下:

下面是我自己用的STM32作为下位机使用SPI跟上位机通信的DEMO,秉承一贯的风格,注释详细到令人发指,就不多解释了。

#include "stm32f10x.h"

volatile u16 data[16];
volatile u8 index = 0;
volatile u8 flag  = 0;  //通知主函数处理IO事件的信号量,1:需要处理,0:不需要处理

volatile u32 time = 0;  //SPI信号中的时间偏移量,过大的话,认为是第二组SPI数据

void SPIInit(void) {
    /* 中断向量配置 */
    NVIC_InitTypeDef nvicInitTypeDef;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    nvicInitTypeDef.NVIC_IRQChannel = SPI1_IRQn;
    nvicInitTypeDef.NVIC_IRQChannelPreemptionPriority = 0;
    nvicInitTypeDef.NVIC_IRQChannelSubPriority = 2;
    nvicInitTypeDef.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvicInitTypeDef);

    /* GPIO 设置 */

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1 | RCC_APB2Periph_AFIO, ENABLE);

    GPIO_InitTypeDef  GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOA , &GPIO_InitStructure);

    /*
     *  SPI 管脚配置
     *  PA4 ---- NSS
     *  PA5 ---- SCK
     *  PA6 ---- MISO
     *  PA7 ---- MOSI
     */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA , &GPIO_InitStructure);

    /* spi config */
    SPI_InitTypeDef spiInitStructure;

    /* spi 初始化定义 */
    spiInitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线读写
    spiInitStructure.SPI_Mode      = SPI_Mode_Slave;                  //从模式
    spiInitStructure.SPI_DataSize  = SPI_DataSize_8b;                 //8位每帧
    spiInitStructure.SPI_CPOL      = SPI_CPOL_Low;                    //时钟空闲为低
    spiInitStructure.SPI_CPHA      = SPI_CPHA_1Edge;                  //数据捕获于第一个时钟沿
    spiInitStructure.SPI_NSS       = SPI_NSS_Hard;                    //硬件控制NSS信号
    spiInitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //波特率预分频
    spiInitStructure.SPI_FirstBit  = SPI_FirstBit_MSB;                //数据传输从MSB位开始
    spiInitStructure.SPI_CRCPolynomial = 7;                           //CRC设置,不启用
    SPI_Init(SPI1, &spiInitStructure);

    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);                  //使能接收中断

    SPI_Cmd(SPI1, ENABLE);                                            //使能SPI1
}

/* SPI 中断控制函数 */
void SPI1_IRQHandler(void) {
    if(SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) == SET){
        u16 buff = 0;
        buff = SPI_I2S_ReceiveData(SPI1);
        if(time > 9000 || index == 0) {
            //时间过大或者index本身为0,认为新数据
            index = 0;
            if(buff == 0xAA){
                //包头            
                data[index] = buff;
                index++;
            }            
        }else if(index == 10){
            //checksum校验

            //校验通过的话,通知主函数处理IO
            index = 0;
            flag  = 1;
        }else if(index < 9 && index >= 1){
            //IO数据中的字节
            data[index] = buff;
            index++;
        }
    }
    time = 0;
}

int main(void) {
    SystemInit();

    index = 0;
    flag  = 0;

    SPIInit();
    GPIO_SetBits(GPIOA, GPIO_Pin_0);

    while(1) {
        time++;
        if(flag){
            flag = 0;
            //处理IO数据
            if(data[1] & 0x0080)
                GPIO_SetBits(GPIOA, GPIO_Pin_0);
            else
                GPIO_ResetBits(GPIOA, GPIO_Pin_0);
        }
    }
}

这个系列写到第七章,STM32相关的知识也就差不多了,以后我接触到更好的知识点,在自己弄明白的基础上,再陆陆续续更新这个系列。

STM32开发入门系列之六:外部中断

STM32几乎每一个GPIO都可以用来做为外部中断使用,但是按照GPIO引脚名称的数字编号进行分组,每组GPIO只能用一个作为外部中断。比如PA0, PB0,PD0…PG0,这一组共6个GPIO,但只可以选择其中一个作为外部中断。你可以同时使用PA0,PB1,PC2,PD3…这些引脚作为外部中断。

下面就是stm32使用PD2作为下降沿外部中断的示例:

exinterrupt.h

/**
 * 初始化外部中断
 */
#include "stm32f10x.h"

void EXTIX_Init(void);

/* 使用PD2,对应中断是EXTI2,定义引脚 */
#define EXTI2_IOU_Line GPIO_PortSourceGPIOD
#define EXTI2_IOP_Line GPIO_PinSource2
#define EXTI2_IOU_RCC  RCC_APB2Periph_GPIOD
#define EXTI2_IOP_RCC  GPIO_Pin_2
#define EXTI2_IOU_GPIO GPIOD

exinterrupt.c

#include "exinterrupt.h"

/**
 * 整个系统的中断向量表:外部中断 > UART中断 > RTC中断
 * 外部中断优先级最高,可以在任何时间嵌套进其他中断执行
 * 使用Group1,抢占优先级1,响应优先级最高7(三位)
 */
void EXTI2_NVIC_Configuration(void) {
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x07;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/**
 * 设置外部中断2引脚位EXTI2_IOU EXTI2_IOP
 * 触发方式下降沿中断
 */
void EXTIX_Init(void) {
    /* GPIO初始化 */
    RCC_APB2PeriphClockCmd(EXTI2_IOU_RCC, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin  = EXTI2_IOP_RCC;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;             /* 上拉输入 */
    GPIO_Init(EXTI2_IOU_GPIO, &GPIO_InitStructure);

    /* 中断结构初始化 */
    GPIO_EXTILineConfig(EXTI2_IOU_Line, EXTI2_IOP_Line);
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line2;                /* 外部中断2 */
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;       /* 中断模式,另一种模式是事件模式 */
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;   /* 下降沿 */
    EXTI_Init(&EXTI_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

    /* 中断向量初始化 */
    EXTI2_NVIC_Configuration();
}

main.c

/* 外部中断2服务程序,最后释放中断标志,让下次中断可以进来 */
void EXTI2_IRQHandler(void){    
    //...your logic code

    EXTI_ClearITPendingBit(EXTI_Line2);     //清除LINE2上的中断标志位  
}

说实话,STM32的外部中断还是相当清晰的。代码的注释很足,不再赘述。下一节我们来介绍一些STM32中的SPI通信。

丁丁生于 1987.07.01 ,30岁,英文ID:newflydd

  • 现居住地 江苏 ● 泰州 ● 姜堰
  • 创建了 Jblog 开源博客系统
  • 坚持十余年的 独立博客 作者
  • 大学本科毕业后就职于 中国电信江苏泰州分公司,前两年从事Oracle数据库DBA工作,两年后公司精简技术人员,被安排到农村担任支局长(其本质是搞销售),于2016年因志向不合从国企辞职,在小城镇找了一份程序员的工作。
  • Git OSChina 上积极参与开源社区