这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » (NRF24L01驱动成功)hanshuyujifen2的 ARM DIY进程贴

共119条 10/12 |‹ 7 8 9 10 11 12 跳转至
高工
2012-05-23 21:18:18     打赏
91楼
SPI初始化和数据传输

LCD的触摸功能是SPI控制的,为了适应LCD的触摸功能,得先搞下SPI。

SPI总线在发送数据的时候,同时也在接收数据。因此将SPI总线的MISO和MOSI两个线连接起来就能看到发送出去的数据。
SPI1对应的GPIO引脚为PA5->SPI1_CLK   PA6->SPI1_MISO PA7->SPI1_MOSI。在板子上分别是SW2、SW3、SW4。因此将SW3、SW4插针链接。SCK不用管。

俺使用了库函数来玩SPI,代码如下:
//SPI初始化
void SPI1_Init(void)
{     
    SPI_InitTypeDef  SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );   
      
    /* Configure SPI1 pins: SCK, MISO and MOSI */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;      //分别对应SPI1_CLK、SPI1_MISO、SPI1_MOSI
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    /* SPI1 configuration */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;        //设置SPI工作模式:设置为主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;        //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;        //选择了串行时钟的稳态:时钟悬空高
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //数据捕获于第二个时钟沿
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;        //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定义波特率预分频的值:波特率预分频值为256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值计算的多项式
    SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
   
    /* Enable SPI1  */
    SPI_Cmd(SPI1, ENABLE); //使能SPI外设
             

//发送/接收数据
u8 SPI1_ReadWriteByte(u8 TxData)
{       
    u8 retry=0;                 
    //while((SPI1->SR&1<<1)==0)//等待发送区空   
    /* Loop while DR register in not emplty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
    {
        retry++;
        if(retry>200)return 0;
    }             
    /* Send byte through the SPI1 peripheral */
    SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
    retry=0;

    /* Wait to receive a byte */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //检查指定的SPI标志位设置与否:接受缓存非空标志位
    {
        retry++;
        if(retry>200)return 0;
    }                                 
    /* Return the byte read from the SPI bus */
    return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据                   
}


主函数中调用:
    SPI1_Init();
    for(i=0;i<100;i++)
    {
        printf("SPI接收到数据:%d\r\n",SPI1_ReadWriteByte(i));
    }

串口显示图片如下:



高工
2012-05-27 09:49:53     打赏
92楼
连上三天晚班。昨天弄触摸屏没搞定

高工
2012-05-27 15:59:37     打赏
93楼
硬件iic弄eeprom,跟踪调试一次,出现问题断电重来。死锁太正常的事情了!断电重新上电就死锁不了了。

高工
2012-05-27 21:05:58     打赏
94楼
完整的FSMC驱动LCD程序

今天本来想把SPI驱动LCD触摸屏整下,搞了半天没眉目。就整理了了下LCD。
一直想把FSMC驱动的程序发出来,今天才有空。比GPIO快不少。
本来想连着SRAM驱动一起发上来,有点事没整完。
实现的功能只是初始化LCD,之后将屏刷成黑色的。
只是演示下LCD的FSMC驱动方法。没别的作用。
至于画点画图什么的,程序中包含这些东西。你可以自己试试
另:我的晶振是12M的,使用8M晶振的童鞋需要改改。方法jobs的帖子里边有,我的帖子里边讲的比较麻烦。

下边是代码链接:
http://share.eepw.com.cn/share/download/id/75286

高工
2012-05-28 21:15:47     打赏
95楼
我没他的教程。你发我?

高工
2012-05-28 21:29:09     打赏
96楼
不懂触摸屏工作原理。有点困难!
手机上网,你的验证码刷了十几次才刷出来的。

高工
2012-05-28 22:26:28     打赏
97楼
网盘的啊,我手机没法看!发那个例子到我邮箱吧hanshuyujifen@yahoo.com.cn

高工
2012-05-29 21:07:30     打赏
98楼
硬件SPI方式读取触屏数据
感谢Rancho的提示。我在弄触屏的时候有一个小错误,片选触屏后没有等待直接就去读数据了。
读出的数据一直都是1006.Rancho提示后,我在片选后给了10us的延时,触屏数据就出来了。
1、SPI初始化
    前面已经给出了,使用的是硬件SPI方式读取数据
2、触屏初始化
完成触屏片选信号初始化,并调用SPI1初始化代码完成SPI初始化
void touchInit(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
     //初始化LCD_CS(PF10)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOF, &GPIO_InitStructure);               

    SPI1_Init();
}

3、从触屏读数据函数
初始化之后就可以从触屏读出数据了(原始的触屏AD转换值)。
向触屏写写0X90 读x坐标数据,写0xD0 读Y坐标数据
u16 read_trouch (u8 cmd)
{
    u16 temp=0;
    u8 i;

    //片选LCD触屏
    SetTouchCS();
    //片选之后一定要延时。否则读不出数据
    delay_us(10);
    SPI1_ReadWriteByte(cmd);        //SPI写命令

    temp=SPI1_ReadWriteByte(0);    //读数

    i=SPI1_ReadWriteByte(0);
    temp<<=8;
    temp|=i;
    temp>>=3;

    //清楚片选
    ClrTouchCS();
    return temp;
}
//读出数据
 void touch_GetAdXY(u16 *x,u16 *y)
{
    *x=read_trouch(CHX);    //写0X90 读x坐标
    *y=read_trouch(CHY);    //写0xD0 读Y坐标
}
注意:读出的数据并不是对应于LCD像素的。需要进行一定的转换后才能使用。读出的数据没有消抖,不是很稳定


4、主函数中调用
        touchInit();
    printf("触摸屏初始化完成\r\n");
   
    while(1)
    {
        touch_GetAdXY(&x,&y);
        if(x!=0)
        printf("%d,%d \r\n",x,y);
        delay_ms(100);

    }
从LCD触屏读数据的图:


5、触屏校准
    待续

6、将触屏读出的数据转换成与LCD像素对于的数据
    待续

高工
2012-05-31 11:23:16     打赏
99楼
串口DMA方式发送数据

需要用到DMA方式处理一些数据。可我对DMA不太了解,上午没事就拿串口开刀。参考原子的代码,做了串口DMA方式发送数据的实验。
以下注释是实验的产物。
1、DMA初始化
void DMA_Configuration(void)
{
    DMA_DeInit(DMA1_Channel4);   //将DMA的通道1寄存器重设为缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;  //DMA外设基地址。DMA将数据写入这个寄存器。写入后直接发送
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;  //DMA内存基地址,在内存中。相当于数据源。
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //外设作为数据传输的目的地。传输方式从内存到外设。USART为外设
    DMA_InitStructure.DMA_BufferSize = sizeof(SendBuff);  //DMA通道的DMA缓存的大小。一次传输的数据量,不大于65535
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //USART1->DR地址是固定的
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增。读一个数据后,自动移动到下一个数据
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //外设数据宽度,即DR的位数
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位。元数据宽度
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Normal;  //工作在循环模式,手动停止
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道,中优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   //不是内存到内存传输。
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
}


2、主函数中调用
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);  //使能UART DMA传输
    /* Enable USART1 TX DMA1 Channel */
    DMA_Cmd(DMA1_Channel4, DISABLE );  //关闭USART1 TX DMA1 所指示的DMA通道     
     //DMA_Configuration();
    DMA_InitStructure.DMA_BufferSize = sizeof(SendBuff);  //DMA通道的DMA缓存的大小
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
    DMA_Cmd(DMA1_Channel4, ENABLE);  //使能USART1 TX DMA1 所指示的通道
    //扫描按键停止DMA
    while(1)
    {
        t=KEY_Scan();
        if(t==1)//KEY0按下   
        {
            USART_DMACmd(USART1, USART_DMAReq_Tx, DISABLE);   
            DMA_Cmd(DMA1_Channel4, DISABLE );
        }
    }

高工
2012-05-31 21:22:47     打赏
100楼
定时器触发DMA方式的DAC

使用定时器触发DAC转换,弄了几天终于搞定。
今天这个实验的整个工作过程是这样的:

1、先定义一个数据源,作为DMA时的数据源
uint8_t Escalator8bit[5000] = {0x0,0x11,0x22,0x33,0x44,0x55, 0x66,0x77,0x88,0x99,0xaa,0xbb,0xCC,0xdd,0xee, 0xFF};

2、配置GPIO  DAC1 DAC2分别对应PA4和PA5
void DAC_GPIOConfig(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       //必须是模拟输出模式
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

3、使能时钟。
GPIOA和DAC不用说了,那是必须的。由于使用了TIM6触发DAC1转换,需要使能TIM6时钟。
使用了DMA2的3号通道,需要使能DMA2时钟。
DAC使用那个通道进行DMA,参看STM32参考手册中文版第148和149页关于DMA请求映像的描述
void DAC_RCCConfig(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);     
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);     //别开错时钟了
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

}

4、TIM6设置
    TIM_PrescalerConfig(TIM6, 0xF, TIM_PSCReloadMode_Update);   //TIM6预分频
    TIM_SetAutoreload(TIM6, 0xFF);                                   //自动装载的值。
    TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);        //产生什么样的触发信号。选择产生更新信号

5、DAC的初始化
    //DAC初始化
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;    //选择触发源
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
    //使能OutputBuffer可提高带负载能力。接耳机,实测:关闭OutputBuffer时声音很小几乎听不见。使能后声音还可以
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);

6、DMA配置
    DMA_DeInit(DMA2_Channel3);   //DMA2的3号通道。必须是这个通道
    DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address;   //外设地址,即DAC通道 1的 8位右对齐数据保持寄存器(DAC_DHR8R1)地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Escalator8bit;  //源数据地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //外设是数据目标
    DMA_InitStructure.DMA_BufferSize = sizeof(Escalator8bit);          //Escalator8bit的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   //禁止外设地址自动增长
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;     //源数据地址自动增长
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //外设数据宽度
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;       //存储器数据宽度
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;        //循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
   
    DMA_Init(DMA2_Channel3, &DMA_InitStructure);

7、之后就可以使能DMA功能和定时器触发了
    DAC_Cmd(DAC_Channel_1, ENABLE);   //使能DAC
    DAC_DMACmd(DAC_Channel_1, ENABLE); //使能DAC的DMA功能

    TIM_Cmd(TIM6, ENABLE);   //使能TIM6  按照手册要求,TIM6必须在DAC使能之后再使能

接上耳机,会听见一个很单调的声音。大约1K不到的频率。
谁玩过MIG 29或者LOCK ON这个游戏的话,会很熟悉这个声音。
8、测试代码
    My_DAC2_Init();
    while(1)
    {
        //printf("当前DAC输出值为:%d mv\r\n",(3300*DAC_GetDataOutputValue(DAC_Channel_1))/4095);
        if(TIM_GetFlagStatus (TIM6,TIM_FLAG_Update) == SUCCESS )
        {
            TIM_ClearFlag(TIM6,TIM_FLAG_Update);
            printf("当前DAC输出值为:%d mv\r\n",(3300*DAC_GetDataOutputValue(DAC_Channel_1))/4095);       
        }
    }
上图:(稍后补上)
我的耳机:

数据图:

共119条 10/12 |‹ 7 8 9 10 11 12 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]