CH376的基础入门:基于ch376与SPI总线 的stm32f103c8t6与典型常用存储设备(TF卡,U盘)的通信

此文章所用学习板出于通用性考虑,
使用了ch376
进行TF卡与f103c8t6的SPI总线进行通信

利用ch376强大的CMD功能
对连接的存储设备进行写入,擦除等一系列操作(此类操作以连接在ch376上的u盘为例)

此文章仅涉及ch376的基础使用方法
更多内容可以关注作者
作者会在其他文章中继续整活

硬件使用基础

在这里插入图片描述
在这里插入图片描述

引脚功能
SCKclock generates the pulse signal
MISOMaster input Service output(can transform the data while the MOSI is running ) duplex capable (全双工通信可行)
MOSIMaster output Service input
NSSdata transform enable(检查位)

NSS
在这里插入图片描述
在这里插入图片描述

  1. one channle can only connect to one SPI service device ,so if more than one devices are in connection ,must use more I/O pins and swap them to NSS mode and connect them to the devices contact
  2. naturally ,every device got the very line to connect ,no need for an address to verify which the device is like I2C or CAN

一号版上的线路连接

CH376基础上 SPI与TF卡的通信
在这里插入图片描述
Header 8X2 的 2-8脚
DO,DI,CLK,CS
负责与ch376进行通信

同时由ch376 的 16-13脚引出到
S DO,S DL,SCLK,S CSS
再次引入到单片机的SPI进行通信
在这里插入图片描述

在这里插入图片描述

库文件准备

需要添加

  • Lib
    stm32f10x_spi.c
  • Basic
    sys.c
    spi.c
  • Hardwawre
    ch376.c
    filesys.c

SPI总线的初始化

spi.c

#include "spi.h"


void SPI2_Init(void){ //SPI2初始化
	SPI_InitTypeDef  SPI_InitStructure;
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//使能SPI_2时钟
	GPIO_InitStructure.GPIO_Pin = SPI2_MISO;  //SPI2的MISO(PB14)为浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(SPI2PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = SPI2_MOSI | SPI2_SCK;	//SPI2的MOSI(PB15)和SCLK(PB13)为复用推免输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(SPI2PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = SPI2_NSS;	 //SPI2的NSS(PB12)为推免输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(SPI2PORT,&GPIO_InitStructure);


	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线输入输出全双工模式,SPI双线并行允许全双工模式运行	
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置为SPI的主机模式(SCK主动产生时钟)(SCK由MASTER产生)
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//SPI数据大小:发送8位帧数据结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//空闲状态时SCK的状态,High为高电平,Low为低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//时钟相位,1表示在SCK的奇数沿边采样,2表示偶数沿边采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS由软件控件片选
	//如果选择SPI_NSS_Hard,则需要在每次通信之前手动设置,且在之后再次手动关闭
	/*
	//在stm32f103x_spi.h中
	//#define SPI_NSS_Hard	  ((uint16_t)0x0000)
	//so we program like yhis
	#if !SPI_InitStructure.SPI_NSS
	#pragram
	
	u8 SPI2_SendByte(u8 Byte)
	{
	GPIO_Pin_12/SPI2_NSS = 0;
	while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
	SPI_I2S_SendData(SPI2,Byte);
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);
	return SPI_I2S_ReceiveData(SPI2);
	SPI2_NSS = 1;
	}

	#endif
	*/


	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//时钟的预分频值(0~256)单个时钟周期的长度,数字越大,通信越慢,信号越稳定
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //MSB高位在前
	SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC较验和的多项式
	SPI_Init(SPI2,&SPI_InitStructure); //初始化SPI2的配置项
	SPI_Cmd(SPI2,ENABLE); //使能SPI2  
}

//SPI2数据发+收程序(主要用于发送)
u8 SPI2_SendByte(u8 Byte){ //通过SPI2口发送1个数据,同时接收1个数据
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET); //如果发送寄存器数据没有发送完,循环等待
	SPI_I2S_SendData(SPI2,Byte);  //往发送寄存器写入要发送的数据
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //如果接受寄存器没有收到数据,循环
	return SPI_I2S_ReceiveData(SPI2);
}

有关
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //MSB高位在前
在这里插入图片描述

spi.h

#ifndef __SPI_H
#define __SPI_H	 
#include "sys.h"

#define SPI2PORT		GPIOB	//定义IO接口
#define SPI2_MOSI		GPIO_Pin_15	//定义IO接口
#define SPI2_MISO		GPIO_Pin_14	//定义IO接口
#define SPI2_SCK		GPIO_Pin_13	//定义IO接口
#define SPI2_NSS		GPIO_Pin_12	//定义IO接口

void SPI2_Init(void);
u8 SPI2_SendByte(u8 Byte);
		 				    
#endif


ch376的使用

command命令的使用与说明

ch376数据手册下载地址
阅读P3的
5、命令
对应
ch376inc.h
头文件

ch376.h

#ifndef __CH376_H
#define __CH376_H	 
#include "sys.h"
#include "spi.h"
#include "delay.h"
#include "ch376inc.h"

#define CH376_INTPORT		GPIOA	//定义IO接口
#define CH376_INT			GPIO_Pin_15	//定义IO接口


void 	CH376_PORT_INIT( void ); /* CH376通讯接口初始化 */
void	xEndCH376Cmd( void ); /* 结束SPI命令 */
void 	xWriteCH376Cmd( u8 mCmd ); /* 向CH376写命令 */
void 	xWriteCH376Data( u8 mData );/* 向CH376写数据 */
u8 	xReadCH376Data( void );	/* 从CH376读数据 */
u8 	Query376Interrupt( void );/* 查询CH376中断(INT#引脚为低电平) */
u8 	mInitCH376Host( void ); /* 初始化CH376 */

		 				    
#endif

以上两个库文件可以保证简单的使用与日后扩展

以下是与spi总线配套的驱动程序

ch376.c
#include "CH376.h"

CH376接口的初始化 与 数据传输函数

/*******************************************************************************
* 函  数  名      : CH376_PORT_INIT
* 描      述      : 由于使用软件模拟SPI读写时序,所以进行初始化.
*                   如果是硬件SPI接口,那么可使用mode3(CPOL=1&CPHA=1)或
*                   mode0(CPOL=0&CPHA=0),CH376在时钟上升沿采样输入,下降沿输出,数
*                   据位是高位在前.
*******************************************************************************/
void CH376_PORT_INIT(void){ //CH376的SPI接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //选择端口号                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
	GPIO_Init(GPIOA,&GPIO_InitStructure);	

	GPIO_SetBits(CH376_INTPORT,CH376_INT); //中断输入脚拉高电平
	GPIO_SetBits(SPI2PORT,SPI2_NSS); //片选接口接高电平
}
/*******************************************************************************
SPI输出8个位数据.    * 发送: u8 d:要发送的数据.
*******************************************************************************/
void Spi376OutByte(u8 d){ //SPI发送一个字节数据 
   SPI2_SendByte(d);	 
}
/*******************************************************************************
* 描      述      : SPI接收8个位数据.  u8 d:接收到的数据.
*******************************************************************************/
u8 Spi376InByte(void){	//SPI接收一个字节数据
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //如果接受寄存器没有收到数据,循环
	return SPI_I2S_ReceiveData(SPI2);
}
/*******************************************************************************
* 描      述      : 向CH376写   数据.
* 输      入      : u8 mData:
*                   要发送的数据.
*******************************************************************************/
void xWriteCH376Data(u8 mData){
	Spi376OutByte( mData );
	delay_us(800);  /* 确保读写周期大于0.6mS */
}
/*******************************************************************************
* 函  数  名      : xReadCH376Data
* 描      述      : 从CH376读数据.
*******************************************************************************/
u8 xReadCH376Data(void){
	u8 i;
	delay_us(10);
	i = SPI2_SendByte(0xFF);
	return(i);
}

CH376的命令函数 与 中断使用函数

/*******************************************************************************
* 描      述      : 向CH376写  命令.
* 输      入      : u8 mCmd:要发送的命令.
*******************************************************************************/
void xWriteCH376Cmd(u8 mCmd){
	GPIO_SetBits(SPI2PORT,SPI2_NSS);   /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */
	delay_us(20);
/* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出
*  方向,SPI_SDO为输入方向 */
	GPIO_ResetBits(SPI2PORT,SPI2_NSS);     /* SPI片选有效 */
	Spi376OutByte( mCmd );  /* 发出命令码 */
	delay_us(1700);   /* 延时1.5mS确保读写周期大于1.5mS,或者用上面一行的状态查询代替 */
}
/*******************************************************************************
* 函  数  名      : xEndCH376Cmd   结束命令.
*******************************************************************************/ 
void xEndCH376Cmd(void){ //结束命令
	GPIO_SetBits(SPI2PORT,SPI2_NSS); //SPI片选无效,结束CH376命令
}
/*******************************************************************************
* 描      述      : 查询CH376中断(INT#低电平).
* 返      回      : 0:无中断.       1:有中断.
*******************************************************************************/
u8 Query376Interrupt(void){
	u8 i;
	i = GPIO_ReadInputDataBit(CH376_INTPORT,CH376_INT); 	
	return( i ); 
}			

Ch376的初始化 与 通信检查
Ps: 前面都是前摇,main.c中只使用这一条

/*******************************************************************************
* 描      述      : 初始化CH376.
* 返      回      : FALSE:无中断.  TRUE:有中断.
*******************************************************************************/
u8 mInitCH376Host(void){
	u8	res;	
	delay_ms(600);
	CH376_PORT_INIT( );           /* 接口硬件初始化 */
	xWriteCH376Cmd( CMD11_CHECK_EXIST );    /* 测试单片机与CH376之间的通讯接口 */
	xWriteCH376Data( 0x55 );
	res = xReadCH376Data( );
//	printf("res =%02x \n",(unsigned short)res);
	xEndCH376Cmd( );
	if ( res != 0xAA ) return( ERR_USB_UNKNOWN );  /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */
	xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */
	xWriteCH376Data( 0x06 ); //06H=已启用的主机方式并且自动产生SOF包  ???
	delay_us(20);
	res = xReadCH376Data( );
//	printf("res =%02x \n",(unsigned short)res);
	xEndCH376Cmd( );

	if ( res == CMD_RET_SUCCESS ){  //RES=51  命令操作成功
	    return( USB_INT_SUCCESS ); //USB事务或者传输操作成功 
	}else{
	    return( ERR_USB_UNKNOWN );/* 设置模式错误 */
	}
}

在以上程序的基础上对TF卡进行通信的程序示例

仅用于TF卡是否连接的检测
数据的读写请继续向下阅读
main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"

#include "spi.h"
#include "ch376.h"
#include"filesys.h"

int main (void){//主程序
	u8 s;
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	TOUCH_KEY_Init();//触摸按键初始化
	RELAY_Init();//继电器初始化

	I2C_Configuration();//I2C初始化
	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   YoungTalk    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(2,"  U DISK TEST   "); //显示字符串
	//CH376初始化	
	SPI2_Init();//SPI接口初始化
	if(mInitCH376Host()== USB_INT_SUCCESS){//CH376初始化
		OLED_DISPLAY_8x16_BUFFER(4,"   CH376 OK!    "); //显示字符串
	}
	while(1){
		s = CH376DiskConnect();	 //读出U盘的状态 
		if(s == USB_INT_SUCCESS){ //检查U盘是否连接//等待U盘插入
			OLED_DISPLAY_8x16_BUFFER(6," U DISK Ready!  "); //显示字符串
		}else{
			OLED_DISPLAY_8x16_BUFFER(6,"                "); //显示字符串		
		} 
		delay_ms(500); //刷新的间隔
	}
}

  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值