chrisrh 发表于 2021-3-18 19:39

【复旦微FM33LC046N评测】+ SPI驱动OLED

本帖最后由 chrisrh 于 2021-3-18 19:37 编辑

<p>使用复旦微FM33LC046N的自带的硬件SPI1或GPIO口模拟SPI控制0.96&#39;OLED</p>

<ol>
        <li>SPI引脚定义</li>
        <li>0.96&rsquo;OLED引脚定义</li>
        <li>硬件HW_SPI驱动OLED</li>
        <li>软件模拟SPI驱动OLED</li>
        <li>一个问题</li>
</ol>

<hr />
<p><strong><span style="color:#c0392b;">1.SPI1引脚</span></strong></p>

<p><span style="background-color:#ffffff;">查阅数据手册可知SPI1的引脚为PB8/9/10/11或PD2/3/4/5,SPI2的引脚为PC7/8/9/10,如下:</span></p>

<p></p>

<hr />
<p><strong><span style="color:#c0392b;">2.0.96&rsquo;OLED引脚定义</span></strong></p>

<p style="margin-left: 40px;">手中的这块OLED没有CS片选引脚,也就一块,所以在连接MCU的时候,SPI只需要配置好MOSI和SCLK就行,</p>

<p style="margin-left: 40px;">MISO和SSN可不进行配置,其中:</p>

<p style="margin-left: 40px;">VCC,DC供电电压3.3~4.3V,如果使用5V电压,为保险起见串一个100~500欧的电阻 &nbsp;<br />
DC,命令数据选择管脚<br />
RES,模块复位管脚&nbsp;<br />
D0(SCLK),时钟脚<br />
D1(MOSI),主输出从输入数据脚</p>

<p></p>

<hr />
<p><span style="color:#c0392b;"><strong>3.硬件HW_SPI驱动OLED</strong></span></p>

<p>使用的是SPI1对应的PB<span style="background-color:#ffffff;">8/9/10/11引脚,将其中的PB9/PB11与OLED相连,其中</span></p>

<p><span style="background-color:#ffffff;">PB9----SCLK</span></p>

<p><span style="background-color:#ffffff;">PB11----MOSI</span></p>

<p>控制引脚这里选用PB6/7,其中:<br />
PB6 ----&gt;DC&nbsp;&nbsp; &nbsp;命令CMD或数据DATA标识控制IO<br />
PB7 ----&gt;RES &nbsp;复位</p>

<p>SPI1初始化函数如下:</p>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">
<pre data-widget="codesnippet">
<code class="language-cpp hljs"><span class="hljs-keyword">void</span> MF_SPI1_Init(<span class="hljs-keyword">void</span>)
{

    FL_GPIO_InitTypeDef    GPIO_InitStruct;
    FL_SPI_InitTypeDef    defaultInitStruct;

    defaultInitStruct.transferMode = FL_SPI_TRANSFER_MODE_FULL_DUPLEX;<span class="hljs-comment">/*! 传输模式 单双工 */</span>
    defaultInitStruct.mode = FL_SPI_WORK_MODE_MASTER;<span class="hljs-comment">/*! 主从模式 */</span>
    defaultInitStruct.dataWidth = FL_SPI_DATA_WIDTH_8B;<span class="hljs-comment">/*! 数据位宽 */</span>
    defaultInitStruct.clockPolarity = FL_SPI_POLARITY_NORMAL;<span class="hljs-comment">/*! 时钟极性 */</span>
    defaultInitStruct.clockPhase = FL_SPI_PHASE_EDGE1;<span class="hljs-comment">/*! 时钟相位 */</span>
    defaultInitStruct.softControl = DISABLE;
<span class="hljs-comment">/*! NSS 脚使能软件控制,enable时:使用SPI需先拉低片选;disable时为硬件控制*/</span>
    defaultInitStruct.baudRate = FL_SPI_BAUDRATE_DIV2;<span class="hljs-comment">/*! 通讯速率 */</span>
    defaultInitStruct.bitOrder = FL_SPI_BIT_ORDER_MSB_FIRST;<span class="hljs-comment">/*! Bit方向 */</span>

    FL_SPI_Init(SPI1,&amp;defaultInitStruct );
               
    FL_SPI_ClearTXBuff(SPI1);
    FL_SPI_ClearRXBuff(SPI1);
               
    GPIO_InitStruct.pin = FL_GPIO_PIN_11|FL_GPIO_PIN_10|FL_GPIO_PIN_9|FL_GPIO_PIN_8;
    GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;
<span class="hljs-comment">//数字外设功能,IO 的输入或输出方向由所连接的外设功能决定</span>
    GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.pull = DISABLE;
    GPIO_InitStruct.remapPin = DISABLE;
    FL_GPIO_Init( GPIOB, &amp;GPIO_InitStruct );
}</code></pre>
<img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" /><span style="background: url(&quot;https://bbs.eeworld.com.cn/static/editor/plugins/widget/images/handle.png&quot;) rgba(220, 220, 220, 0.5); top: -15px; left: 0px; display: block;"><img height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" /></span></div>

<p>其中GPIO初始化,模式选择为FL_GPIO_MODE_DIGITAL,</p>

<p>FL_GPIO_MODE_DIGITAL:即数字外设功能,IO 的输入或输出方向由所连接的外设功能决定;</p>

<p>&nbsp;</p>

<p></p>

<p>配置PB6/7两个控制IO口的初始化其中:<br />
PB6 ----&gt;DC&nbsp;&nbsp; &nbsp;命令CMD或数据DATA标识控制IO<br />
PB7 ----&gt;RES &nbsp;复位</p>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">
<pre data-widget="codesnippet">
<code class="hljs language-cpp"><span class="hljs-comment">//初始化两个控制IO口</span>
<span class="hljs-comment">//PB6 ----&gt;DC        命令CMD或数据DATA标识控制IO</span>
<span class="hljs-comment">//PB7 ----&gt;RES复位</span>
<span class="hljs-keyword">void</span> MF_SPI_GPIOPin_Init(<span class="hljs-keyword">void</span>)
{
    FL_GPIO_InitTypeDef    GPIO_InitStruct;
    GPIO_InitStruct.pin= FL_GPIO_PIN_6|FL_GPIO_PIN_7;
    GPIO_InitStruct.mode = FL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.pull = DISABLE;
    GPIO_InitStruct.remapPin = DISABLE;
    FL_GPIO_Init( GPIOB, &amp;GPIO_InitStruct );
               
        FL_GPIO_SetOutputPin(GPIOB, FL_GPIO_PIN_6|FL_GPIO_PIN_7);
}</code></pre>
<img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" /><span style="background: url(&quot;https://bbs.eeworld.com.cn/static/editor/plugins/widget/images/handle.png&quot;) rgba(220, 220, 220, 0.5); top: -15px; left: 0px; display: block;"><img height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" /></span></div>

<p>通过SPI向OLED中写入命令或数据,当片选SSN配置为软件控制使能时,使用SPI时需先拉低片选,待写完数据后,再拉高SSN;若为硬件控制,可不配置SSN::</p>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">
<pre data-widget="codesnippet">
<code class="hljs language-cpp"><span class="hljs-comment">//SPI写一个字节</span>
<span class="hljs-keyword">void</span> SPI_WriteByte(uint8_t data)
{
    <span class="hljs-comment">//FL_SPI_SetSSNPin(SPI1, FL_SPI_SSN_LOW);</span>
   
    <span class="hljs-keyword">while</span> (!(FL_SPI_IsActiveFlag_TXBuffEmpty(SPI1)));
<span class="hljs-comment">//Get SPI TX Buffer Empty Flag,State of bit (1 or 0)</span>
                FL_SPI_WriteTXBuff(SPI1, data);
    <span class="hljs-keyword">while</span> (!(FL_SPI_IsActiveFlag_RXBuffFull(SPI1)));
       
        <span class="hljs-comment">//FL_SPI_SetSSNPin(SPI1, FL_SPI_SSN_HIGH);</span>
}

<span class="hljs-comment">//向OLED中写命令或数据</span>
<span class="hljs-keyword">void</span> OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{
        <span class="hljs-keyword">if</span>(cmd)
        {
                SPI_OLED_DC_Set();
        }
        <span class="hljs-keyword">else</span>
        {
                SPI_OLED_DC_Clr();
        }
        SPI_WriteByte(dat);
}</code></pre>
<img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" /><span style="background: url(&quot;https://bbs.eeworld.com.cn/static/editor/plugins/widget/images/handle.png&quot;) rgba(220, 220, 220, 0.5); top: -15px; left: 0px; display: block;"><img height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" /></span></div>

<p>配置完驱动函数后,调用FL_SPI_Enable(SPI1)启动SPI;</p>

<p></p>

<p>&nbsp;</p>

<hr />
<p>&nbsp;</p>

<p><span style="color:#c0392b;"><strong>4.软件模拟SPI驱动OLED</strong></span></p>

<p>硬件SPI资源有限,且IO口较为集中,为了更好的利用资源,可以使用软件模拟SPI控制OLED</p>

<p>选用PC5/6/7/8模拟SPI通信控制OLED,其中:</p>

<p>PC7----DC,命令数据选择管脚<br />
PC9----RES,模块复位管脚&nbsp;<br />
PC6----D0(SCLK),时钟脚<br />
PC5----D1(MOSI),主输出从输入数据脚</p>

<p>宏定义各IO口,以便移植:</p>

<div aria-label="代码段 小部件" contenteditable="false" role="region" tabindex="-1">
<pre data-widget="codesnippet">
<code class="hljs language-cpp">        <span class="hljs-preprocessor">#define OLED_CMD0        <span class="hljs-comment">//写命令</span></span>
        <span class="hljs-preprocessor">#define OLED_DATA 1        <span class="hljs-comment">//写数据</span></span>

        <span class="hljs-comment">//使用PC5、6、7、9模拟SPI控制OLED</span>
        <span class="hljs-comment">//控制引脚DC</span>
        <span class="hljs-preprocessor">#define AN_SPI_DC_PIN                                 FL_GPIO_PIN_7</span>
        <span class="hljs-preprocessor">#define AN_SPI_DC_GPIOX                                GPIOC</span>
        <span class="hljs-comment">//复位引脚RES</span>
        <span class="hljs-preprocessor">#define AN_SPI_RES_PIN                                 FL_GPIO_PIN_9</span>
        <span class="hljs-preprocessor">#define AN_SPI_RES_GPIOX                        GPIOC</span>
        <span class="hljs-comment">//时钟SCLK/D0</span>
        <span class="hljs-preprocessor">#define AN_SPI_SCLK_PIN                        FL_GPIO_PIN_6 </span>
        <span class="hljs-preprocessor">#define AN_SPI_SCLK_GPIOX                        GPIOC</span>
        <span class="hljs-comment">//数据SDATA/D1</span>
        <span class="hljs-preprocessor">#define AN_SPI_SDA_PIN                 FL_GPIO_PIN_5</span>
        <span class="hljs-preprocessor">#define AN_SPI_SDA_GPIOX                        GPIOC</span>
       
        <span class="hljs-comment">//片选CS:控制单片OLED未使用</span>
        <span class="hljs-comment">//#define AN_SPI_CS_PIN                 FL_GPIO_PIN_8</span>
        <span class="hljs-comment">//#define AN_SPI_CS_GPIOX                        GPIOC</span>
       
        <span class="hljs-preprocessor">#define OLED_RES_Clr()   FL_GPIO_ResetOutputPin(AN_SPI_RES_GPIOX, AN_SPI_RES_PIN)</span>
                <span class="hljs-comment">//Set pin output 0//低电平复位</span>
        <span class="hljs-preprocessor">#define OLED_RES_Set()   FL_GPIO_SetOutputPin(AN_SPI_RES_GPIOX, AN_SPI_RES_PIN)</span>
                <span class="hljs-comment">//Set pin output 1//保持</span>

        <span class="hljs-preprocessor">#define OLED_DC_Clr()    FL_GPIO_ResetOutputPin(AN_SPI_DC_GPIOX, AN_SPI_DC_PIN)</span>
                <span class="hljs-comment">//Set pin output 0//命令模式</span>
        <span class="hljs-preprocessor">#define OLED_DC_Set()    FL_GPIO_SetOutputPin(AN_SPI_DC_GPIOX, AN_SPI_DC_PIN)</span>
                <span class="hljs-comment">//Set pin output 1//数据模式</span>

        <span class="hljs-preprocessor">#define OLED_CS_Clr()    FL_GPIO_ResetOutputPin(AN_SPI_CS_GPIOX, AN_SPI_CS_PIN)</span>
                <span class="hljs-comment">//Set pin output 0</span>
        <span class="hljs-preprocessor">#define OLED_CS_Set()    FL_GPIO_SetOutputPin(AN_SPI_CS_GPIOX, AN_SPI_CS_PIN)</span>
                <span class="hljs-comment">//Set pin output 1</span>
       
        <span class="hljs-preprocessor">#define OLED_SCLK_Clr()FL_GPIO_ResetOutputPin(AN_SPI_SCLK_GPIOX, AN_SPI_SCLK_PIN)</span>
                <span class="hljs-comment">//Set pin output 0</span>
        <span class="hljs-preprocessor">#define OLED_SCLK_Set()FL_GPIO_SetOutputPin(AN_SPI_SCLK_GPIOX, AN_SPI_SCLK_PIN)</span>
                <span class="hljs-comment">//Set pin output 1</span>

        <span class="hljs-preprocessor">#define OLED_SDA_Clr()       FL_GPIO_ResetOutputPin(AN_SPI_SDA_GPIOX, AN_SPI_SDA_PIN)</span>
                <span class="hljs-comment">//Set pin output 0</span>
        <span class="hljs-preprocessor">#define OLED_SDA_Set()   FL_GPIO_SetOutputPin(AN_SPI_SDA_GPIOX, AN_SPI_SDA_PIN)</span>
                <span class="hljs-comment">//Set pin output 1</span></code></pre>
<img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" /><span style="background: url(&quot;https://bbs.eeworld.com.cn/static/editor/plugins/widget/images/handle.png&quot;) rgba(220, 220, 220, 0.5); top: -15px; left: 0px; display: block;"><img height="15" role="presentation" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" title="点击并拖拽以移动" width="15" /></span></div>

<p>模拟SPI通信写一个字节:</p>

<pre>
<code class="language-cpp">void AN_OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{       
    uint8_t i;                          
    if(cmd)   OLED_DC_Set();
    else      OLED_DC_Clr();                  
    for(i=0;i&lt;8;i++)
    {                          
          OLED_SCLK_Clr();
          if(dat&amp;0x80)   OLED_SDA_Set();
          else         OLED_SDA_Clr();
          OLED_SCLK_Set();
          dat&lt;&lt;=1;   
    }                                              
    OLED_DC_Set();             
} </code></pre>

<p>模拟SPI通信写数据和写命令:</p>

<pre>
<code class="language-cpp">void OLED_WrDat(unsigned char data)
{
        unsigned char i=8;

    OLED_DC_Set();
          __NOP();
    OLED_SCLK_Clr();
    __NOP();
while(i--)
{
    if(data&amp;0x80)
          {OLED_SDA_Set();}
    else
          {OLED_SDA_Clr();}
          OLED_SCLK_Set();
   
    __NOP();
            
    OLED_SCLK_Clr();   
    data&lt;&lt;=1;   
}
}

void OLED_WrCmd(unsigned char cmd)
{
        unsigned char i=8;
       
   OLED_DC_Clr() ;
   OLED_SCLK_Clr();

   __NOP();
   
   while(i--)
   {
    if(cmd&amp;0x80)
          {OLED_SDA_Set();}
    else
          {OLED_SDA_Clr();}

    OLED_SCLK_Set();
    __NOP();
            
    OLED_SCLK_Clr();   
    cmd&lt;&lt;=1;;   
}        
}</code></pre>

<p>在main中初始化调用:</p>

<p></p>

<p>&nbsp;</p>

<hr />
<p><strong><span style="color:#c0392b;">5.一个问题</span></strong></p>

<p>和UART+DMA调试的时候一样,设备收到的是正常的,但在Debug中看到的数据不对,</p>

<p>用keil的Debug在线调试SPI收发时,在watch或者memory中看到的数据与实际要收发的数据不一致,</p>

<p>(如SPI发送一组数据到OLED/别的设备,在Debug中看到数组中的数或实时更新的数与要传送的数不同)</p>

<p>不知道是为什么&middot;&middot;&middot;</p>

<p>&nbsp;</p>

<p><strong><span style="color:#c0392b;">程序见附件,仅供参考&middot;&middot;&middot;&middot;&middot;&middot;</span></strong></p>

Jacktang 发表于 2021-3-18 22:14

<p>debug调试时,要查看SPI总线寄存器</p>

freebsder 发表于 2021-3-18 23:21

<p>这个debug有点神奇了。具体描述一下呢。</p>

nmg 发表于 2021-3-19 09:28

<p>呼叫 @doudou52098 帮忙</p>

chrisrh 发表于 2021-3-19 10:29

Jacktang 发表于 2021-3-18 22:14
debug调试时,要查看SPI总线寄存器

<p>是看指向APB_bus上的Transmit shift reg吗</p>

chrisrh 发表于 2021-3-19 10:32

freebsder 发表于 2021-3-18 23:21
这个debug有点神奇了。具体描述一下呢。

<p>这是最初用SPI参考例程debug看的</p>

<p>估计是我没有配置好或者没有找对相应的寄存器吧</p>

littleshrimp 发表于 2021-3-19 17:36

<p>调试不一致降一下优化等级试试,还有如果是DMA你卡断点的时候收到信数据一般内存的数据还是会变化的。</p>
页: [1]
查看完整版本: 【复旦微FM33LC046N评测】+ SPI驱动OLED