Gorgon_Meducer 发表于 2013-10-11 19:36:01

[交流][拍砖]新版驱动接口风格调查(一):IO配置

本帖最后由 Gorgon_Meducer 于 2013-11-6 18:36 编辑

说在前面的话
    这是新版本编码规范的一个范例代码,包含对应的驱动模型和接口规范,我希望在不提供进一步解说的情况下听听大家
的意见,并提供以下信息:
1、第一眼给你的感受,是喜欢 恐惧 还是 茫然无措
2、顺次阅读代码后,代码要表达的意思你是否已经了解大概,表意是否清晰
3、有什么你觉得疑惑的地方?
4、有什么改进意见?
5、任何批评意见都是非常欢迎的

如果可能,希望大家能在回帖中描述下你理解的代码的行为。

这个库的目标就是要让代码使用起来简单,不仅仅要屏蔽底层的寄存器操作,还要做到功能和意义一目了然。
参与的人,即便自觉是菜鸟,也不用觉得自己水平不够之类的,因为你们就是最终的用户,你们是最有发言权的!
非常感谢大家的参与。


P.S: 这个库将是未来一个实质性的半导体产品的系统库。

傻孩子风格
支持我的朋友们,谢谢你们!
    IO_CFG (
      /*Target PinIO Functions                            IO Properties                   */
            {PA1,       IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA1,       IO_WORKS_AS_SPI1_MOSI & IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},

            {PA2,       IO_WORKS_AS_SPI0_SCK& IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA2,       IO_WORKS_AS_SPI1_SCK& IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},

            {PA3,       IO_WORKS_AS_SPI0_CS   & IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA3,       IO_WORKS_AS_SPI1_CS   & IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},

            {PA4,       IO_WORKS_AS_SPI0_MISO & IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA4,       IO_WORKS_AS_SPI1_MISO & IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
    );

    SPI_CFG (
      SPI0,
      SPI_MODE_MASTER             |
      SPI_MODE_FORMAT_SPI         |
      SPI_MODE_CLK_IDLE_HIGH      |
      SPI_MODE_SAMP_SECOND_EDGE   |
      SPI_MODE_NOT_LOOP_BACK      |
      SPI_MODE_SLAVE_OUT_ENABLE,
      SPI_DATASIZE_16,

      SPI_PCLK_DIV_32
    );

    SPI_CFG (
      SPI1,
      SPI_MODE_SLAVE            |
      SPI_MODE_FORMAT_SPI         |
      SPI_MODE_CLK_IDLE_HIGH      |
      SPI_MODE_SAMP_SECOND_EDGE   |
      SPI_MODE_NOT_LOOP_BACK      |
      SPI_MODE_SLAVE_OUT_ENABLE,
      SPI_DATASIZE_16,
      SPI_PCLK_DIV_32
    );

    //! enable spi, spi is ready to work, the spi is standby...
    SPI0.Open();
    SPI1.Open();

    uint16_t s_hwMasterOut = 0x1234;
    uint16_t s_hwMasterIn = 0;
    uint16_t s_hwSlaveOut = 0x5678;
    uint16_t s_hwSlaveIn = 0;

    while (fsm_rt_cpl != SPI1.DataExchange(s_hwSlaveOut,&s_hwSlaveIn)) {
      SPI0.DataExchange(s_hwMasterOut,&s_hwMasterIn);
    }

    SPI0.Close();
    SPI1.Close();
经典C风格
喜欢原汁原味的朋友们,多提意见阿!
    do {
      io_cfg_t tCFG[] = {
      /*Target PinIO Functions                            IO Properties                   */
            {PA1,       IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA1,       IO_WORKS_AS_SPI1_MOSI & IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},

            {PA2,       IO_WORKS_AS_SPI0_SCK& IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA2,       IO_WORKS_AS_SPI1_SCK& IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},

            {PA3,       IO_WORKS_AS_SPI0_CS   & IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA3,       IO_WORKS_AS_SPI1_CS   & IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},

            {PA4,       IO_WORKS_AS_SPI0_MISO & IO_READ_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
            {PA4,       IO_WORKS_AS_SPI1_MISO & IO_WRITE_ONLY,IO_PULL_UP | IO_RAW_LOOP_BACK},
      };
      IO.Config(tCFG, UBOUND(tCFG));
    } while(0);

    do {
      spi_cfg_t tCFG = {
            SPI_MODE_MASTER             |
            SPI_MODE_FORMAT_SPI         |
            SPI_MODE_CLK_IDLE_HIGH      |
            SPI_MODE_SAMP_SECOND_EDGE   |
            SPI_MODE_NOT_LOOP_BACK      |
            SPI_MODE_SLAVE_OUT_ENABLE,
            SPI_DATASIZE_16,

            SPI_PCLK_DIV_32
      };
      SPI0.Init(&tCFG);
    } while(0);

    do {
      spi_cfg_t tCFG = {
            SPI_MODE_SLAVE            |
            SPI_MODE_FORMAT_SPI         |
            SPI_MODE_CLK_IDLE_HIGH      |
            SPI_MODE_SAMP_SECOND_EDGE   |
            SPI_MODE_NOT_LOOP_BACK      |
            SPI_MODE_SLAVE_OUT_ENABLE,
            SPI_DATASIZE_16,

            SPI_PCLK_DIV_32
      };
      SPI1.Init(&tCFG);
    } while(0);

    //! enable spi, spi is ready to work, the spi is standby...
    SPI0.Open();
    SPI1.Open();

    uint16_t s_hwMasterOut = 0x1234;
    uint16_t s_hwMasterIn = 0;
    uint16_t s_hwSlaveOut = 0x5678;
    uint16_t s_hwSlaveIn = 0;

    while (fsm_rt_cpl != SPI1.DataExchange(s_hwSlaveOut,&s_hwSlaveIn)) {
      SPI0.DataExchange(s_hwMasterOut,&s_hwMasterIn);
    }

    SPI0.Close();
    SPI1.Close();

Gorgon_Meducer 发表于 2013-10-11 19:56:07

本帖最后由 Gorgon_Meducer 于 2013-12-19 19:20 编辑

一些大家可能会感兴趣的定义:

device.h
//! \brief IO functions
//! @{
typedef enum io_func_sel_t {
    IO_WORKS_AS_GPIO      = 0x0000,
    IO_WORKS_AS_FUNC_0      = 0x0000,
    IO_WORKS_AS_FUNC_1      = 0x0100,
    IO_WORKS_AS_FUNC_2      = 0x0200,
    IO_WORKS_AS_FUNC_3      = 0x0300,
    IO_WORKS_AS_CSC         = 0x0300,
    IO_WORKS_AS_ANALOG      = 0x0200,


    //! APIO0~7
    IO_WORKS_AS_APIO0       = 0x0300 | __IO_RW,
    IO_WORKS_AS_APIO1       = 0x0301 | __IO_RW,
    IO_WORKS_AS_APIO2       = 0x0302 | __IO_RW,
    IO_WORKS_AS_APIO3       = 0x0303 | __IO_RW,
    IO_WORKS_AS_APIO4       = 0x0304 | __IO_RW,
    IO_WORKS_AS_APIO5       = 0x0305 | __IO_RW,
    IO_WORKS_AS_APIO6       = 0x0306 | __IO_RW,
    IO_WORKS_AS_APIO7       = 0x0307 | __IO_RW,

    //! I2C0
    IO_WORKS_AS_I2C0_SCL    = 0x0308 | __IO_RW,
    IO_WORKS_AS_I2C0_SDA    = 0x0309 | __IO_RW,

    //! SPI0
    IO_WORKS_AS_SPI0_SCK    = 0x030A | __IO_RW,
    IO_WORKS_AS_SPI0_MISO   = 0x030B | __IO_RW,
    IO_WORKS_AS_SPI0_MOSI   = 0x030C | __IO_RW,
    IO_WORKS_AS_SPI0_CS   = 0x030D | __IO_RW,

    //! SPI1
    IO_WORKS_AS_SPI1_SCK    = 0x030E | __IO_RW,
    IO_WORKS_AS_SPI1_MISO   = 0x030F | __IO_RW,
    IO_WORKS_AS_SPI1_MOSI   = 0x0310 | __IO_RW,
    IO_WORKS_AS_SPI1_CS   = 0x0311 | __IO_RW,

    //! USART0
    IO_WORKS_AS_USART0_SCLK = 0x0312 | __IO_RW,
    IO_WORKS_AS_USART0_TXD= 0x0313 | __IO_WO,
    IO_WORKS_AS_USART0_RXD= 0x0314 | __IO_RO,
    IO_WORKS_AS_USART0_RTS= 0x0315 | __IO_WO,
    IO_WORKS_AS_USART0_DTR= 0x0316 | __IO_WO,
    IO_WORKS_AS_USART0_CTS= 0x0317 | __IO_RO,
    IO_WORKS_AS_USART0_DCD= 0x0318 | __IO_RO,
    IO_WORKS_AS_USART0_DSR= 0x0319 | __IO_RO,
    IO_WORKS_AS_USART0_RI   = 0x031A | __IO_RO,

    //! USART1
    IO_WORKS_AS_USART1_SCLK = 0x031B | __IO_RW,
    IO_WORKS_AS_USART1_TXD= 0x031C | __IO_WO,
    IO_WORKS_AS_USART1_RXD= 0x031D | __IO_RO,
    IO_WORKS_AS_USART1_DIR= 0x031E | __IO_WO,
    IO_WORKS_AS_USART1_BAUD = 0x031F | __IO_WO,
   
    //! APIO8~15
    IO_WORKS_AS_APIO8       = 0x0320 | __IO_RW,
    IO_WORKS_AS_APIO9       = 0x0321 | __IO_RW,
    IO_WORKS_AS_APIO10      = 0x0322 | __IO_RW,
    IO_WORKS_AS_APIO11      = 0x0323 | __IO_RW,
    IO_WORKS_AS_APIO12      = 0x0324 | __IO_RW,
    IO_WORKS_AS_APIO13      = 0x0325 | __IO_RW,
    IO_WORKS_AS_APIO14      = 0x0326 | __IO_RW,
    IO_WORKS_AS_APIO15      = 0x0327 | __IO_RW,

    //! I2C1
    IO_WORKS_AS_I2C1_SCL    = 0x0328 | __IO_RW,
    IO_WORKS_AS_I2C1_SDA    = 0x0329 | __IO_RW,

    //! SPI2
    IO_WORKS_AS_SPI2_SCK    = 0x032A | __IO_RW,
    IO_WORKS_AS_SPI2_MISO   = 0x032B | __IO_RW,
    IO_WORKS_AS_SPI2_MOSI   = 0x032C | __IO_RW,
    IO_WORKS_AS_SPI2_CS   = 0x032D | __IO_RW,

    //! I2S
    IO_WORKS_AS_I2S0_TXMCLK = 0x032E | __IO_WO,
    IO_WORKS_AS_I2S0_TXCLK= 0x032F | __IO_RW,
    IO_WORKS_AS_I2S0_TXWS   = 0x0330 | __IO_RW,
    IO_WORKS_AS_I2S0_TXD    = 0x0331 | __IO_WO,
    IO_WORKS_AS_I2S0_RXMCLK = 0x0332 | __IO_WO,
    IO_WORKS_AS_I2S0_RXCLK= 0x0333 | __IO_RW,
    IO_WORKS_AS_I2S0_RXWS   = 0x0334 | __IO_RW,
    IO_WORKS_AS_I2S0_RXD    = 0x0335 | __IO_RO,

    //! USART2
    IO_WORKS_AS_USART2_SCLK = 0x0336 | __IO_RW,
    IO_WORKS_AS_USART2_TXD= 0x0337 | __IO_WO,
    IO_WORKS_AS_USART2_RXD= 0x0338 | __IO_RO,
    IO_WORKS_AS_USART2_DIR= 0x0339 | __IO_WO,

    //! USART3
    IO_WORKS_AS_USART3_SCLK = 0x033A | __IO_RW,
    IO_WORKS_AS_USART3_TXD= 0x033B | __IO_WO,
    IO_WORKS_AS_USART3_RXD= 0x033C | __IO_RO,
    IO_WORKS_AS_USART3_DIR= 0x033D | __IO_WO,

}em_io_func_sel_t;
//! @}
io.h


#ifndef __GSP_SPI_DRIVER_H__
#define __GSP_SPI_DRIVER_H__

/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"
#include "..\device.h"
#include ".\i_io_spi.h"

/*============================ MACROS ========================================*/
//! \brief Macro for extern spi interface function prototypes
#define __EXTERN_SPI_INTERFACE(__N, __DATA)                                     \
    extern bool   spi##__N##_init(spi_cfg_t *ptSspCfg);                     \
    extern bool   spi##__N##_idle(void);                                    \
    extern fsm_rt_t spi##__N##_open(void);                                    \
    extern fsm_rt_t spi##__N##_close(void);                                     \
    extern fsm_rt_t spi##__N##_data_exchange(uint16_t hwOut,uint16_t *phwIn);   \
    extern bool   spi##__N##_write(uint16_t hwOut);                           \
    extern bool   spi##__N##_read(uint16_t *phwIn);                           

/*============================ MACROFIED FUNCTIONS ===========================*/

#define SPI_CFG(__SPI,...)                                                      \
    do {                                                                        \
      spi_cfg_t tCFG = {                                                      \
            __VA_ARGS__                                                         \
      };                                                                      \
      __SPI.Init(&tCFG);                                                      \
    } while(false)

/*============================ TYPES =========================================*/

//! \name spi working mode
//! @{
typedef enum {
    SPI_MODE_MASTER             = 0x00,             //!< select master mode
    SPI_MODE_SLAVE            = _BV(0),         //!< select slave mode
         
    SPI_MODE_FORMAT_SPI         = 0x00,             //!< use standard spi fram
    SPI_MODE_FORMAT_TI          = _BV(1),
    SPI_MODE_FORMAT_MICROWIRE   = _BV(2),
   
    SPI_MODE_CLK_IDLE_HIGH      = _BV(3),         //!< SCK is high in idle
    SPI_MODE_CLK_IDLE_LOW       = 0x00,             //!< SCK is low in idle
   
    SPI_MODE_SAMP_FIRST_EDGE    = 0x00,             //!< sample at first edge of sck
    SPI_MODE_SAMP_SECOND_EDGE   = _BV(4),         //!< sample at second edge of sck
   
    SPI_MODE_LOOP_BACK          = _BV(5),         //!< enable loop back
    SPI_MODE_NOT_LOOP_BACK      = 0x00,             //!< default disable loop back
   
    SPI_MODE_SLAVE_OUT_ENABLE   = 0x00,             //!< default enable slave output
    SPI_MODE_SLAVE_OUT_DISABLE= _BV(6)            //!< disable slave output
}em_spi_mode_t;
//! @}

//! \name spi datasize
//! @{
typedef enum {
    SPI_MODE_DATASIZE_4         = 0x3,            //!< datasize is 4 bits
    SPI_MODE_DATASIZE_5         = 0x4,            //!< datasize is 5 bits
    SPI_MODE_DATASIZE_6         = 0x5,            //!< datasize is 6 bits
    SPI_MODE_DATASIZE_7         = 0x6,            //!< datasize is 7 bits
    SPI_MODE_DATASIZE_8         = 0x7,            //!< datasize is 8 bits
    SPI_MODE_DATASIZE_9         = 0x8,            //!< datasize is 9 bits
    SPI_MODE_DATASIZE_10      = 0x9,            //!< datasize is 10 bits
    SPI_MODE_DATASIZE_11      = 0xA,            //!< datasize is 11 bits
    SPI_MODE_DATASIZE_12      = 0xB,            //!< datasize is 12 bits
    SPI_MODE_DATASIZE_13      = 0xC,            //!< datasize is 13 bits
    SPI_MODE_DATASIZE_14      = 0xD,            //!< datasize is 14 bits
    SPI_MODE_DATASIZE_15      = 0xE,            //!< datasize is 15 bits
    SPI_MODE_DATASIZE_16      = 0xF               //!< datasize is 16 bits
}em_spi_datasize_t;
#define __SPI_CLK_DIV(__N,__Value)   \
            SPI_PCLK_DIV_##__N = (2*(__N)),

//! \name enum spi clk div
//! @{
typedef enum{
   MREPEAT(127,__SPI_CLK_DIV ,NULL)
} em_spiclk_div_t;
//! @}

//! \name spi configuration structure
//! @{
typedef struct{
    uint16_t    hwMode;                           //!< spi working mode
    uint8_t   chDataSize;                         //!< frame size
    em_spiclk_div_t   chClockDiv;                        //!< P_CLK prescaler
} spi_cfg_t;
//! @}

//! \name class: spi_t
//! @{
DEF_INTERFACE(spi_t)
    bool      (*Init)(spi_cfg_t *ptCfg);          //!< initialize spi
    bool      (*Idle)(void);                      //!< get busy flag status
    fsm_rt_t    (*Open)(void);                      //!< open spi
    fsm_rt_t    (*Close)(void);                     //!< close spi
    //! method for data exchange
    fsm_rt_t    (*DataExchange)(uint16_t hwOut,uint16_t *phwIn);
    bool      (*Write)(uint16_t hwOut);         //!< write data to spi buffer
    bool      (*Read)(uint16_t *phwIn);         //!< read data from spi buffer
    spi_reg_t * const ptRegPage;                  //!< reference to register page
END_DEF_INTERFACE(spi_t)
//! @}

/*============================ PROTOTYPES ====================================*/
//! \brief spi interface function prototypes
MREPEAT(SPI_COUNT, __EXTERN_SPI_INTERFACE, NULL)

/*============================ GLOBAL VARIABLES ==============================*/
//! \brief SPI objects
extern ROOT const spi_t SPI;

/*============================ LOCAL VARIABLES ===============================*/

#endif
spi.c



/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"
#include ".\i_io_spi.h"
#include "..\device.h"
#include "..\pm\pm.h"
/*============================ MACROS ========================================*/

//! \brief The reference for current object
#define this      (*ptThis)

/*============================ MACROFIED FUNCTIONS ===========================*/
//! \brief Macro for spi init
#define __SPI__SPI_T_INIT(__N,__DATA)                                           \
    {                                                                           \
      ((spi_reg_t *)(SPI##__N##_BASE_ADDRESS)),                               \
      0,                                                                      \
      0                                                                     \
    },
   
//! \brief Macro for spi interface init
#define __SPI_OBJ(__N,__A)                                                      \
    {                                                                           \
      &spi##__N##_init,                                                       \
      &spi##__N##_idle,                                                       \
      &spi##__N##_open,                                                       \
      &spi##__N##_close,                                                      \
      &spi##__N##_data_exchange,                                              \
      &spi##__N##_write,                                                      \
      &spi##__N##_read,                                                       \
      ((spi_reg_t *)(SPI##__N##_BASE_ADDRESS)),                               \
    },

//! \brief Macro for spi interface function prototypes
#define __SPI_INTERFACE_PROTOTYPES(__N,__DATA)                                  \
    extern bool spi##__N##_init(spi_cfg_t *ptSpiCfg);                           \
    extern bool spi##__N##_idle(void);                                          \
    extern fsm_rt_t spi##__N##_open(void);                                    \
    extern fsm_rt_t spi##__N##_close(void);                                     \
    extern fsm_rt_t spi##__N##_data_exchange(uint16_t hwOut,uint16_t *phwIn);   \
    extern bool spi##__N##_write(uint16_t hwOut);                               \
    extern bool spi##__N##_read(uint16_t *phwIn);

//! \brief Macro of spi modules interface function body
#define __SPI_INTERFACE(__N,__DATA)                                             \
    bool spi##__N##_init(spi_cfg_t *ptSpiCfg)                                 \
    {                                                                           \
      return spi_init((__spi_t *)&__SPI,ptSpiCfg,__N);       \
    }                                                                           \
    bool spi##__N##_idle(void)                                                \
    {                                                                           \
      return spi_is_idle((__spi_t *)&__SPI, __N);            \
    }                                                                           \
    fsm_rt_t spi##__N##_open(void)                                              \
    {                                                                           \
      return spi_open((__spi_t *)&__SPI,__N);                \
    }                                                                           \
    fsm_rt_t spi##__N##_close(void)                                             \
    {                                                                           \
      return spi_close((__spi_t *)&__SPI,__N);               \
    }                                                                           \
    fsm_rt_t spi##__N##_data_exchange(uint16_t hwOut,uint16_t *phwIn)         \
    {                                                                           \
      return spi_data_exchange((__spi_t *)&__SPI,hwOut,phwIn);         \
    }                                                                           \
    bool spi##__N##_write(uint16_t hwOut)                                       \
    {                                                                           \
      return spi_write((__spi_t *)&__SPI,hwOut);                         \
    }                                                                           \
    bool spi##__N##_read(uint16_t *phwIn)                                       \
    {                                                                           \
      return spi_read((__spi_t *)&__SPI,phwIn);                        \
    }      
/*============================ TYPES =========================================*/
#define __SPI_CLK_DIV(__N,__Value)   \
            SPI_PCLK_DIV_##__N = (2*(__N)),

//! \name enum spi clk div
//! @{
typedef enum{
   MREPEAT(127,__SPI_CLK_DIV ,NULL)
} em_spiclk_div_t;
//! @}

//! \name spi configuration structure
//! @{
typedef struct{
    uint16_t    hwMode;                           //!< spi working mode
    uint8_t   chDataSize;                         //!< frame size
    em_spiclk_div_t   chClockDiv;               //!< P_CLK prescaler
} spi_cfg_t;
//! @}

//! \name class: spi_t
//! @{
DEF_INTERFACE(spi_t)
   
    bool      (*Init)(spi_cfg_t *ptCfg);          //!< initialize spi
    bool      (*Idle)(void);                      //!< get busy flag status
    fsm_rt_t    (*Open)(void);                      //!< open spi
    fsm_rt_t    (*Close)(void);                     //!< close spi
    //! method for data exchange
    fsm_rt_t    (*DataExchange)(uint16_t hwOut,uint16_t *phwIn);
    bool      (*Write)(uint16_t hwOut);         //!< write data to spi buffer
    bool      (*Read)(uint16_t *phwIn);         //!< read data from spi buffer
    spi_reg_t * const ptRegPage;                  //!< reference to register page
END_DEF_INTERFACE(spi_t)
//! @}

//! \name internal class
//! @{
DEF_CLASS(__spi_t)
    spi_reg_t * const ptREG;                        //!< reference to register page
    uint8_t chStateClose;                           //!< state variable for Close
    uint8_t chStateExchange;                        //!< state variable for Exchange
END_DEF_CLASS(__spi_t)
//! @}

//! \name spi working mode
//! @{
typedef enum {
    SPI_MODE_MASTER             = 0x00,             //!< select master mode
    SPI_MODE_SLAVE            = _BV(0),         //!< select slave mode
         
    SPI_MODE_FORMAT_SPI         = 0x00,             //!< use standard spi fram
    SPI_MODE_FORMAT_TI          = _BV(1),
    SPI_MODE_FORMAT_MICROWIRE   = _BV(2),
   
    SPI_MODE_CLK_IDLE_HIGH      = _BV(3),         //!< SCK is high in idle
    SPI_MODE_CLK_IDLE_LOW       = 0x00,             //!< SCK is low in idle
   
    SPI_MODE_SAMP_FIRST_EDGE    = 0x00,             //!< sample at first edge of sck
    SPI_MODE_SAMP_SECOND_EDGE   = _BV(4),         //!< sample at second edge of sck
   
    SPI_MODE_LOOP_BACK          = _BV(5),         //!< enable loop back
    SPI_MODE_NOT_LOOP_BACK      = 0x00,             //!< default disable loop back
   
    SPI_MODE_SLAVE_OUT_ENABLE   = 0x00,             //!< default enable slave output
    SPI_MODE_SLAVE_OUT_DISABLE= _BV(6)            //!< disable slave output
}em_spi_mode_t;
//! @}

//! \name spi datasize
//! @{
typedef enum {
    SPI_MODE_DATASIZE_4         = 0x3,            //!< datasize is 4 bits
    SPI_MODE_DATASIZE_5         = 0x4,            //!< datasize is 5 bits
    SPI_MODE_DATASIZE_6         = 0x5,            //!< datasize is 6 bits
    SPI_MODE_DATASIZE_7         = 0x6,            //!< datasize is 7 bits
    SPI_MODE_DATASIZE_8         = 0x7,            //!< datasize is 8 bits
    SPI_MODE_DATASIZE_9         = 0x8,            //!< datasize is 9 bits
    SPI_MODE_DATASIZE_10      = 0x9,            //!< datasize is 10 bits
    SPI_MODE_DATASIZE_11      = 0xA,            //!< datasize is 11 bits
    SPI_MODE_DATASIZE_12      = 0xB,            //!< datasize is 12 bits
    SPI_MODE_DATASIZE_13      = 0xC,            //!< datasize is 13 bits
    SPI_MODE_DATASIZE_14      = 0xD,            //!< datasize is 14 bits
    SPI_MODE_DATASIZE_15      = 0xE,            //!< datasize is 15 bits
    SPI_MODE_DATASIZE_16      = 0xF               //!< datasize is 16 bits
}em_spi_datasize_t;
//! @}

/*============================ PROTOTYPES ====================================*/
//! \brief spi interface function prototypes
MREPEAT(SPI_COUNT, __SPI_INTERFACE_PROTOTYPES, NULL)

/*============================ GLOBAL VARIABLES ==============================*/

//! \brief SPI object
ROOT const spi_t SPI[] = {
    MREPEAT(SPI_COUNT, __SPI_OBJ, NULL)
};

/*============================ LOCAL VARIABLES ===============================*/

//! \brief internal spi object
static CLASS(__spi_t) __SPI[] = {
    //__SPI__SPI_T_INIT(0, 0xFF)
    MREPEAT(SPI_COUNT, __SPI__SPI_T_INIT, 0xFF)
};


#define SAFE_CLK_CODE(...)      \
    {\
      ahbclk_status_t tAHBStatus;\
      pclk_status_t tPCLKStatus;\
      tAHBStatus = PM_GET_AHB_CLK_STATUS((em_ahb_clk_t)(AHBCLK_SPI0 + chIndex ));\
      tPCLKStatus = PM_GET_PCLK_STATUS(PCLK_SPI0 + chIndex);\
      PM_AHB_CLK_ENABLE((em_ahb_clk_t)(AHBCLK_SPI0 + chIndex ));\
      PM_PCLK_CFG((PCLK_SPI0 + chIndex),1);\
      __VA_ARGS__;\
      PM_AHB_CLK_RESUME((em_ahb_clk_t)(AHBCLK_SPI0 + chIndex ),tAHBStatus);\
      PM_PCLK_RESUME((PCLK_SPI0 + chIndex),tPCLKStatus);\
    }

/*! \brief initialize spi
*! \param ptSpiCfg spi configuration object
*! \retval true initialization succeed
*! \retval false initialization failed
*/
static bool spi_init(__spi_t *ptSPI,spi_cfg_t *ptSpiCfg,uint8_t chIndex)
{
   
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
    bool bResult = false;

    //! validate input parameter
    if((ptThis == NULL) || (ptSpiCfg == NULL)) {
      return false;
    }

    SAFE_CLK_CODE (
      do {
            spi_reg_t *ptREG = this.ptREG;

            //!read CR0 register
            uint32_t wTempCR0 = ptREG->CR0.Value & ~(
                                             SPI_CR0_DSS_MSK   |
                                             SPI_CR0_FRF_MSK   |
                                             SPI_CR0_CPOL_MSK    |
                                             SPI_CR0_CPHA_MSK    |
                                             SPI_CR0_SCR_MSK
                                             );

            //! read CR1 register
            uint32_t wTempCR1 = ptREG->CR1.Value & ~(
                                             SPI_CR1_LBM_MSK   |
                                             SPI_CR1_MS_MSK      |
                                             SPI_CR1_SOD_MSK
                                             );
            
            uint32_t wTempCPSR = ptREG->CPSR.Value & ~(SPI_CPSR_CPSDVSR_MSK);

            /* -------------------- Modify Configuration Begin------------------- */
            //! switch between master mode and slave mode
            if (ptSpiCfg->hwMode & SPI_MODE_SLAVE) {
                //! use slave mode
                wTempCR1 |= SPI_MODE_SET(SPI_SLAVE);
            } /* else {
                //! use master mode
            }*/

            //! set frame type
            if (ptSpiCfg->hwMode & SPI_MODE_FORMAT_TI) {   
                //!< use TI fram
                wTempCR0 |= SPI_FORMAT_SET(SPI_FORMAT_TI);
            } else if(ptSpiCfg->hwMode & SPI_MODE_FORMAT_MICROWIRE) {
                //! use Microwire
                wTempCR0 |= SPI_FORMAT_SET(SPI_FORMAT_MICROWIRE);
            } /*else {
                //! use standard type
            }*/

            //! set clock polarity and sample point
            if (ptSpiCfg->hwMode & SPI_MODE_CLK_IDLE_HIGH) {
                wTempCR0 |= SPI_CLK_POLARITY_SET(SPI_CLK_IDLE_HIGH);
            }/*else {
                //! use idle low type
            }*/
            if (ptSpiCfg->hwMode & SPI_MODE_SAMP_SECOND_EDGE) {
                wTempCR0 |= SPI_CLK_SAMP_SET(SPI_SAMP_SECOND_EDGE);
            }/*else {
                //! samp at first edge
            }*/
      
            //! set as loop-back mode
            if (ptSpiCfg->hwMode & SPI_MODE_LOOP_BACK) {
                wTempCR1 |= SPI_LOOP_BACK_SET(SPI_LOOP_BACK);
            }/*else {
                //! standard mode
            }*/
            
            //! if slave, data out disable
            if (ptSpiCfg->hwMode & SPI_MODE_SLAVE_OUT_DISABLE) {
                wTempCR1 |= SPI_SLAVE_OUT_SET(SPI_OUT_DISABLE);
            }/*else {
                //! standard mode
            }*/
            
            //! set data size
            if (    (ptSpiCfg->chDataSize < SPI_MODE_DATASIZE_4)
                ||(ptSpiCfg->chDataSize > SPI_MODE_DATASIZE_16)) {
                bResult = false;
                break;
            } else {
                wTempCR0 |= SPI_DATASIZE_SET(ptSpiCfg->chDataSize);
            }
                  
            //! set clock prescaler
            if((ptSpiCfg->chClockDiv < 2) || (ptSpiCfg->chClockDiv > 254)) {
                bResult = false;
                break;
            } else {
                wTempCPSR |= SPI_CPSR_SET(ptSpiCfg->chClockDiv & 0xFFFE);
            }
            
            /* -------------------- Modify Configuration End -------------------- */

            //! update CR0
            ptREG->CR0.Value = wTempCR0;
            //! update CR1
            ptREG->CR1.Value = wTempCR1;
            //! update CPSR
            ptREG->CPSR.Value = wTempCPSR;
            
      } while (false);
    )

    return bResult;
}



/*! \brief return state of spi
*! \param void
*! \retval true spi is idle
*! \retval false spi is busy
*/
static bool spi_is_idle(__spi_t *ptSPI,uint8_t chIndex)
{
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
    bool bResult = true;
    //! validate input parameter
    if(ptThis == NULL) {
      return false;
    }

    SAFE_CLK_CODE (
      //! spi is busy, return false
      if (this.ptREG->SR.Value & SPI_SR_BSY_MSK) {
            bResult = false;
      }
    )

    //! spi is idle, return true
    return bResult;
}


/*! \brief enable spi
*! \param void
*! \retval fsm_rt_cpl spi enabled
*! \retval fsm_rt_err illegal input pointer
*/
static fsm_rt_t spi_open(__spi_t *ptSPI,uint8_t chIndex)
{
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
    // enable AHBCLK
    PM_AHB_CLK_ENABLE((em_ahb_clk_t)(AHBCLK_SPI0 + chIndex ));
    PM_PCLK_CFG(PCLK_SPI0 + chIndex,1);
    //! validate input parameter
    if(ptThis == NULL) {
      return fsm_rt_err;
    }
   
    //! set spi enable bit
    //this.ptREG->CR1.Value &=~ SPI_CR1_SSE_MSK;
    this.ptREG->CR1.Value |= SPI_ENABLE_SET(SPI_ENABLE);

    return fsm_rt_cpl;
}


/*! \brief disable spi
*! \param void
*! \retval fsm_rt_cpl close prosess is complete
*! \retval fsm_rt_on_going close prosess is on going
*! \retval fsm_rt_err illegal input pointer
*/
static fsm_rt_t spi_close(__spi_t *ptSPI,uint8_t chIndex)
{
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
   
    //! validate input parameter
    if(ptThis == NULL) {
      return fsm_rt_err;
    }
   
    //! wait for spi fifo empty
    if(this.ptREG->SR.Value & SPI_SR_TFE_MSK) {
      //! disable spi module
      this.ptREG->CR1.Value &=~ SPI_CR1_SSE_MSK;
      //this.ptREG->CR1.Value |= SPI_ENABLE_SET(SPI_DISABLE);
      // Disable AHBCLK
      PM_AHB_CLK_DISABLE((em_ahb_clk_t)(AHBCLK_SPI0 + chIndex ));
      PM_PCLK_CFG(PCLK_SPI0 + chIndex,0);
      return fsm_rt_cpl;
    }
    return fsm_rt_on_going;
}



#define SPI_DATA_EXCHANGE_START      0
#define SPI_DATA_EXCHANGE_SEND         1
#define SPI_DATA_EXCHANGE_RECEIVE      2

#define SPI_DATA_EXCHANGE_FSM_RST()\
    do{\
      ptThis->chStateExchange = 0;\
    }while(0)

/*! \brief exchange data once
*! \param hwOut the out-data
*! \param *hwIn the addr of in-data
*! \retval fsm_rt_on_going exchange is on going
*! \retval fsm_rt_cpl exchange is complete
*! \retval fsm_rt_err illegal input pointer
*/
static fsm_rt_t spi_data_exchange(__spi_t *ptSPI,uint16_t hwOut,uint16_t *phwIn)
{
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
   
    //! validate input parameter
    if(ptThis == NULL) {
      return fsm_rt_err;
    }

    switch(this.chStateExchange) {
      //! start
      case SPI_DATA_EXCHANGE_START:
            this.chStateExchange = SPI_DATA_EXCHANGE_SEND;
            //break;
      //! send data out
      case SPI_DATA_EXCHANGE_SEND:
            if(this.ptREG->SR.Value & SPI_SR_TNF_MSK) {    //!< tx fifo is not full
                this.ptREG->DR.Value = hwOut;
                this.chStateExchange = SPI_DATA_EXCHANGE_RECEIVE;
            }
            break;
      //! read data in
      case SPI_DATA_EXCHANGE_RECEIVE:
            if(this.ptREG->SR.Value & SPI_SR_RNE_MSK) {    //!< rx fifo is not empty
                uint16_t hwTemp;
                hwTemp = this.ptREG->DR.Value;
                if(phwIn != NULL) {
                  *phwIn = hwTemp;
                }
                SPI_DATA_EXCHANGE_FSM_RST();
                return fsm_rt_cpl;
            }
            break;
    }

    return fsm_rt_on_going;
}


/*! \brief write a data with out recieving
*! \param hwOut the out-data
*! \retval true write access is success
*! \retval false write access is failed or illegal input parameter
*/
static bool spi_write(__spi_t *ptSPI,uint16_t hwOut)
{
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
   
    //! validate input parameter
    if(ptThis == NULL) {
      return false;
    }
    //! if tx fifo is not full, write data
    if(this.ptREG->SR.Value & SPI_SR_TNF_MSK) {            //!< tx fifo is not full
      this.ptREG->DR.Value = hwOut;
      return true;
    }

    return false;
}

/*! \brief read a data with out sending
*! \param phwIn the addr of in-data
*! \retval true read access is success
*! \retval false read access is failed or illegal input parameter
*/
static bool spi_read(__spi_t *ptSPI,uint16_t *phwIn)
{
    CLASS(__spi_t) *ptThis = (CLASS(__spi_t) *)ptSPI;
   
    //! validate input parameter
    if(ptThis == NULL) {
      return false;
    }
    //! if rx fifo is not empty, read data
    if(this.ptREG->SR.Value & SPI_SR_RNE_MSK) {            //!< rx fifo is not empty
      uint16_t hwTemp;
      hwTemp = this.ptREG->DR.Value;
      if(phwIn != NULL) {
            *phwIn = hwTemp;
      }
      return true;
    }

    return false;
}

                                                               

//! \brief Spi modules interface function body
MREPEAT(SPI_COUNT, __SPI_INTERFACE, NULL)


//! end of file

tiger5 发表于 2013-10-11 20:29:01

代码中是不是还像以前是VB还是DELPHI风格?

帮顶。


hdd961140543 发表于 2013-10-11 20:30:08

抢个沙发!
个人感觉和STM32的库差不多,不知道效率怎么样?
是专用的库,还是通用的?

spy2008 发表于 2013-10-11 20:33:18

习惯了{}都新起一行,第一眼看到上面的{},感觉挺官方的。

eduhf_123 发表于 2013-10-11 20:37:43

配置了两个SPI以及它们所使用的IO:一个主模式、一个从模式、对应功能使用相同引脚(通过内部开关矩阵实现?);

启动两个SPI;

然后使用状态机模式做了一个类似于TCP/IP中127.0.0.1的环回测试?

Gorgon_Meducer 发表于 2013-10-11 20:43:26

eduhf_123 发表于 2013-10-11 20:37 static/image/common/back.gif
配置了两个SPI以及它们所使用的IO:一个主模式、一个从模式、对应功能使用相同引脚(通过内部开关矩阵实现 ...

没错,风格上呢?
宜用性上呢?觉得是否容易掌握?

Gorgon_Meducer 发表于 2013-10-11 20:44:49

hdd961140543 发表于 2013-10-11 20:30 static/image/common/back.gif
抢个沙发!
个人感觉和STM32的库差不多,不知道效率怎么样?
是专用的库,还是通用的? ...

库是专用的,但是这个风格本身并不是专用的,很容易为别的芯片编写驱动。
只是因为没有专门的外设支持,有一些这个库的特性会发挥不出来。

Gorgon_Meducer 发表于 2013-10-11 20:45:26

spy2008 发表于 2013-10-11 20:33 static/image/common/back.gif
习惯了{}都新起一行,第一眼看到上面的{},感觉挺官方的。

哈哈……就是官方库……——某种意义上

Gorgon_Meducer 发表于 2013-10-11 20:46:15

hdd961140543 发表于 2013-10-11 20:30 static/image/common/back.gif
抢个沙发!
个人感觉和STM32的库差不多,不知道效率怎么样?
是专用的库,还是通用的? ...

效率上……我主持开发的库——这个可以做信用担保不?

spy2008 发表于 2013-10-11 20:48:38

Gorgon_Meducer 发表于 2013-10-11 20:45 static/image/common/back.gif
哈哈……就是官方库……——某种意义上

{:smile:}配套自己的芯片吗?如果是的话,值得期待。

zhexuejia 发表于 2013-10-11 20:49:34

NICE。。。。。。。

Gorgon_Meducer 发表于 2013-10-11 20:50:27

tiger5 发表于 2013-10-11 20:29 static/image/common/back.gif
代码中是不是还像以前是VB还是DELPHI风格?

帮顶。

传统的C风格也是可以继续用的,只是我想更简洁一些,举例来说,给你看两个宏:

io.h
//! \name macros for general i/o configuration
//! @{
#define IO_CFGdo {\
                  io_cfg_t tCFG[] = {
#define END_IO_CFG};\
                  IO.Config(tCFG,UBOUND(tCFG));\
                } while(0);
//! @}
也就是说,上面的代码中出现的IO_CFG和END_IO_CFG其实只是一个C的代码结构,展开如下:
do {
    io_cfg_t tCFG[] = {
      {PA1, IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA1, IO_WORKS_AS_SPI1_MOSI & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA2, IO_WORKS_AS_SPI0_SCK& IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA2, IO_WORKS_AS_SPI1_SCK& IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA3, IO_WORKS_AS_SPI0_CS   & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA3, IO_WORKS_AS_SPI1_CS   & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA4, IO_WORKS_AS_SPI0_MISO & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA4, IO_WORKS_AS_SPI1_MISO & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
    };
    IO.Configure(tCFG, UBOUND(tCFG));
} while(0);
用户完全可以继续坚持主流的C风格。只是我觉得,用IO_CFG可以节省一点不必要的文字输入。

Gorgon_Meducer 发表于 2013-10-11 20:51:00

spy2008 发表于 2013-10-11 20:48 static/image/common/back.gif
配套自己的芯片吗?如果是的话,值得期待。

Something big is incoming...

zhexuejia 发表于 2013-10-11 20:51:49

看起来很舒服,简洁明了 搬个小板凳坐着好好的学习

Gorgon_Meducer 发表于 2013-10-11 20:53:38

zhexuejia 发表于 2013-10-11 20:51 static/image/common/back.gif
看起来很舒服,简洁明了 搬个小板凳坐着好好的学习

从示例代码上有没有挖掘出别的什么信息?

winster321 发表于 2013-10-11 21:03:54

看着挺清晰,一个小疑问:
{PA1, IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK}中间的逗号按啥属性划分?
IO_PULL_UP是电气上的,LOOP_BACK应该是功能上,或在一起有些别扭。能够很清楚从代码得到电路结构就perfect了

huxiaoping 发表于 2013-10-11 21:05:27

这代码某些地方很像ATMEL的代码封装风格,难道为ATMEL工作的?

eduhf_123 发表于 2013-10-11 21:06:54

Gorgon_Meducer 发表于 2013-10-11 20:43 static/image/common/back.gif
没错,风格上呢?
宜用性上呢?觉得是否容易掌握?

其他都还好,就是那个常数9太惹眼了。

它的作用应该是pCLK / (2 * (N+1))运算得到SPI的波特率吧?

eduhf_123 发表于 2013-10-11 21:08:21

另外,那个常数26不知道做什么用的,好魔幻的两个数字。
{:titter:}

Gorgon_Meducer 发表于 2013-10-11 21:12:24

eduhf_123 发表于 2013-10-11 21:06 static/image/common/back.gif
其他都还好,就是那个常数9太惹眼了。

它的作用应该是pCLK / (2 * (N+1))运算得到SPI的波特率吧? ...

眼睛比较毒辣,这个问题我也纠结了好久,正在想办法封装掉。

Gorgon_Meducer 发表于 2013-10-11 21:14:48

本帖最后由 Gorgon_Meducer 于 2013-10-11 21:20 编辑

winster321 发表于 2013-10-11 21:03 static/image/common/back.gif
看着挺清晰,一个小疑问:
{PA1, IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BAC ...

实际上的确是按照模块的物理划分来分类的
三个部分分别是
{<PIN的名字>, <PIN的目标功能>, Optional <PIN的属性>}

PIN的目标功能就不用我多说了,PIN的属性就是一些IO控制器上的设置阿,比如,上拉,开漏,输入反向,输出反向,输入滤波,高输出强度之类的

有些IO的配置是不需要第三个部分的,比如
IO_CFG
    {PA1, IO_WORKS_AS_USART0_RX},
    {PA2, IO_WORKS_AS_USART0_TX}
END_IO_CFG
也可以将IO配置为普通的GPIO,例如
IO_CFG
    {PA1, IO_WORKS_AS_GPIO, IO_OPEN_DRAIN },
    {PA2, IO_WORKS_AS_GPIO, IO_PULLUP},
END_IO_CFG
谢谢你的意见

Gorgon_Meducer 发表于 2013-10-11 21:15:11

huxiaoping 发表于 2013-10-11 21:05 static/image/common/back.gif
这代码某些地方很像ATMEL的代码封装风格,难道为ATMEL工作的?

不是……ASF处于被鄙视的阶段

eduhf_123 发表于 2013-10-11 21:27:28

这个问题我也想过,像下面这样实现,你觉得如何?#if ((__SYS_PCLK)/(SPI_BPS)/2)*2*(SPI_BPS) != (__SYS_PCLK)
        #error Band rate specified can NOT be implemented!
#else
        #if (__SYS_PCLK)/(SPI_BPS)/2-1 > __SPI_BRG_MAX
                #error Band rate specified is too small!
        #endif
#endif

eduhf_123 发表于 2013-10-11 21:32:36

不过这样来做的话,对用户来说,波特率的设置是变直观了,但代价就是非整数的波特率无法实现了。
{:dizzy:}

Gorgon_Meducer 发表于 2013-10-11 21:46:48

eduhf_123 发表于 2013-10-11 21:27 static/image/common/back.gif
这个问题我也想过,像下面这样实现,你觉得如何?

哪个芯片的?

eduhf_123 发表于 2013-10-11 21:54:19

Gorgon_Meducer 发表于 2013-10-11 21:46 static/image/common/back.gif
哪个芯片的?

不针对具体芯片,只是做这样的处理:

用户(程序员)不用自己计算波特率发生器寄存器的值,而是直接指定波特率;
编译器根据用户指定的波特率和外设时钟频率,自己计算这个值,并检查是否是整数分频、分频系数是否过大。

dr2001 发表于 2013-10-11 22:23:22

本帖最后由 dr2001 于 2013-10-11 22:27 编辑

供参考:
1、首要的,这是C代码片段。所以,对于块结构的封装最好能提供花括号对{}。引入其它语言的块结构定义方法容易引起不适,因为语法高亮/缩进等等都可能不好用,并且寻找配对的操作也是非C的。有花括号以及周边的说明性包围文字,是一个能够更舒适的方案。当然,在其它的实现方面,它可能就不会那么优美。
但这毕竟是C,我们不能说这里要按照A语言的定义方法来,那里要用B语言的方法来,blabla。

2、1L的代码中用同样的形式展现了两种不同的功能:IO_CFG是定义配置并且执行配置操作;SPI_CFG看起来只是进行了配置本身,而没有进行任何配置操作。
换句话说,不清楚地了解内情并阅读头文件,必然会导致某种功能上的混淆。这种不一致性极大影响了库的可读性和可用性。

附加的一点,如果我有多个外设共享一组IO Pin,显然GPIO的配置需要随着外设的开闭对应配置。这里这个功能的实现是不干净的。

Define_Something_Only {
};
这个和结构体定义等 保存信息 的结构有一致性。

Define_Something_and_Action {
} Action_Name(Para_if_Required);
这里表达1 定义信息; 2 利用信息执行操作。而函数式的结构也是C中执行操作的暗示。

而且,这两种表达自身也是一致的,有没有Action而已。作用域问题需要统一规定一下即可,这不是麻烦事儿。

3、SPI_CFG显然是对几个配置REG的组装。用户要特别注意某些要用`|`连接,有些要用`,`分割,而且给出的名称前缀都是SPI_MODE。我确认这点上,只看DataSheet不看库的相关详细说明,绝对没办法用。
至少,用户要能从常量名中分出来哪些是需要`|`的,哪些是需要`,`分割的。

4、定义里,IO_BYPASS_FILTER,IO_FILTER_2CLK,这种不对称的写法用自动完成纯属搞不自在。外设_功能_选项这样的话大家都要统一么。
所以,名称可能变成了 外设-位置/功能-选项值 这样的命名方式。
例如:
IO_Name, IO_Mode1_A | IO_Mode1_B | ..., IO_Mode2_X | IO_Mode2_Y
反正有自动完成,长点就长点吧。。。尽管我不喜欢长名字。


所以,
对于写代码的人来说,这种代码风格有额外的成本,用起来并不舒适。直接配置REG的话,都在看DS查配置了,直接写REG=Func就行了;要么就是调用一个又一个的函数配置即可。这些都不构成额外的记忆和查找负担;而且,有自动完成加持,效率不低。
1L代码所需的额外负担在写代码时,带来的好处并不显著。

- 对于读代码的人来说,除了IO配置外,外设部分未能增加明显的代码可读性,因为本来配外设就会配在一起,上下文是连贯的。
附加的,如果SPI中间要改波特率,比如,操作SD卡,开始用400k通信,卡启动成功后要提频率到50M,配置的写法反而不一定好看。

IO配置完全可以写成列是IO Pin Name,行是不同功能的表格形式,用个SEL/X表示功能使用了没有,一点不比写IO_CFG这样的东西麻烦,可读性也很赞。

- 1L这种风格适用于图形界面进行选择,自动生成的配置代码,可以假设为Keil MDK的Configurator的升级版,把互斥选项什么的都加进去,这样。
并且,输出的东西把默认选项(0Bit的含义)也都写清楚。否则,没看到太多的好处。



总之,1L的代码风格,我认为没有带来非常明显的好处,实用价值有限。至少,对于C的,可以有的已知的,明晰的表达方式而言,没显著的改善。

tiger5 发表于 2013-10-11 22:23:53

谢。
是不是以前说的仿LINUX的READ,WRITE,IOCTL等块,文件等等控制写法?
学习

dr2001 发表于 2013-10-11 22:25:51

eduhf_123 发表于 2013-10-11 21:54 static/image/common/back.gif
不针对具体芯片,只是做这样的处理:

用户(程序员)不用自己计算波特率发生器寄存器的值,而是直接指定 ...

由于C的宏能实现的逻辑有限,所以静态代码很难实现完全自动的需求;总会有多少不能搞定的犄角。
如果用C++的模板或者引入M4再预处理一下,可能可以,不过没细研究过了,有点MetaProgramming的意思了。

eduhf_123 发表于 2013-10-11 22:40:08

dr2001 发表于 2013-10-11 22:23 static/image/common/back.gif
供参考:
1、首要的,这是C代码片段。所以,对于块结构的封装最好能提供花括号对{}。引入其它语言的块结构 ...

我来尝试下,用如下两个关键词的组合来帮楼主“四两拨千斤”,看能拨开您扔来的这块“大石头(至少对楼主来说,您扔来的确实是石头、不是玉)”不?
{:lol:} {:lol:} {:lol:}

关键词来了:Infineon、 DAVE

eduhf_123 发表于 2013-10-11 22:57:03

dr2001 发表于 2013-10-11 22:25 static/image/common/back.gif
由于C的宏能实现的逻辑有限,所以静态代码很难实现完全自动的需求;总会有多少不能搞定的犄角。
如果用C+ ...

元编程太高深了,我完全没概念,要不是看你提起,我都不知道还有这么个东西存在。

编程这个事情,除去编译这个事情以外,本来是程序员和CPU来分工完成的,所有其他事情,要么是程序员做、要么是CPU来做。

可是自从有了“预处理器”这个神奇的玩意儿以后,事情就不一样了:本应该程序员做的事情中,有那么一部分就可以交给预处理器了;而且在程序员的干预下,有一部分本应由CPU做的事情现在也可以由预处理器来处理了。

自从意识到这个问题,我就偶尔地、自觉不自觉地,想着把需要自己做的事情里的一部分推给预处理器——我本懒人,汗一个。
{:sweat:}

Gorgon_Meducer 发表于 2013-10-11 23:27:19

本帖最后由 Gorgon_Meducer 于 2013-10-11 23:54 编辑

dr2001 发表于 2013-10-11 22:23 static/image/common/back.gif
供参考:
1、首要的,这是C代码片段。所以,对于块结构的封装最好能提供花括号对{}。引入其它语言的块结构 ...

非常给力的回复,非常感谢。

对于第一条,说的非常中肯,没什么好说的,直接采纳。谢谢

第二条,SPI_CFG和IO_CFG一样都作了操作的,不是简单的对寄存器的封装,所以一致性上是没有问题的
#define SPI_CFG(__SPI)                      \
    do {                                    \
      spi_cfg_t tSpiCfg = {

#define END_SPI_CFG(__SPI)                  \
      };                                  \
      __SPI.Init(&tSpiCfg);               \
    } while(false);
后面我也会针对第一条对其进行修改,加入{},因此,需要把宏修改为
#define SPI_CFG(__SPI)                      \
    do {                                    \
      spi_cfg_t tSpiCfg =

#define END_SPI_CFG(__SPI)                  \
      ;                                  \
      __SPI.Init(&tSpiCfg);               \
    } while(false);
对于第三条和第四条,你说的非常对,实际上驱动现在只是完成了Beta版,由于是团队合作的结果,不同人在很多
细节上存在不统一,不过我们会在后续阶段对所有的命名问题进行认真的清理,避免这种自讨苦吃的问题。
根据你的建议,我觉得最直接的修改就是将
IO_BYPASS_FILT修改为IO_FILT_BYPASS,并在后续其他命名上遵循统一的命名结构——比如你说的
对象+属性+属性值 的方式,避免进一步的混乱。

实际上,应该将用“,”分开的部分用不同的前缀来区分,比如这里应该将SPI_MODE_DATASIZE_16 修改为
SPI_DATASIZE_16,

这个库有一个基本原则就是不要让用户看DS就能完成所有工作。

另外,风格上的确在为后面的代码生成器做准备,但同时也兼顾用户的阅读。库的设计上注意到一般人有从过去
的代码或者范例代码 Copy and Paste的习惯,所以对于XXX_CFG的结构,我们会保证完整的设置项,比如有
XXXX_ENABLE 就一定会有 XXXX_DISABLE,或者会把不用的部分 /* */ 掉——从而保证代码的 Template 特性。

我承认,如果从0开始写代码,而不是已有的Template来Copy代码,的确需要查阅对应的.h,而且有点繁琐。
但这个 “最初代码”的问题,会被后面的代码生成器解决掉,所以算是绕了过去。

这么说,这个代码风格追求的是——配置部分只要能容易读懂容易修改就可以了——因为是代码生成器负责的,
而具体的Method部分,则要做到尽可能的接近面向对象,简单直接,比如SPI0.Open() SPI0.DataExchange()这类。
因为这是用户要实际经常打交道的部分。

至于你说的中途更换波特率,有两个方法可以解决——1、SPI库提供对应的Property,可以直接通过Set来设置
通过Get来获取(着类似C#的Property);2、通过Template直接粘贴配置,修改里面的值就可以了——也很简单。

总的来说,这个风格高度依赖代码生成器,同时为用户使用具体的功能作了高度的抽象和优化。




B.T.W 我在一楼更新了spi.h的内容,欢迎拍砖。








dr2001 发表于 2013-10-12 07:37:47

Gorgon_Meducer 发表于 2013-10-11 23:27 static/image/common/back.gif
非常给力的回复,非常感谢。

对于第一条,说的非常中肯,没什么好说的,直接采纳。谢谢


对于SPI的init, open, close的具体意义,由于涉及到具体芯片的外设配置以及IO功能分配,不好定论。
我对其行为的期望一般如下:定义具有Idle和Working两个状态的状态机:init类似Reset,从未知状态和Working回到Idle,是不可逆/单向的;Open是Idle到Work;Close是Work到Idle。Work里可以定义到别的运行子态的操作。
所以,至少我对SPI CFG默认INIT,之后再OPEN/CLOSE的具体行为会有疑问。这需要文档额外说明这样的东西。

既然是要做代码生成器,就变成输出的东西怎么格式赞,清晰好看,说明性强了,呵呵。

dr2001 发表于 2013-10-12 07:44:29

eduhf_123 发表于 2013-10-11 22:57 static/image/common/back.gif
元编程太高深了,我完全没概念,要不是看你提起,我都不知道还有这么个东西存在。

编程这个事情,除去编 ...

对于C/C++来说,实际上可以有5步:
编程人员的预估、计算;M4等宏文本处理器提供的计算;C/C++预处理器的计算;编译器编译阶段的常量表达式计算和优化,C++的模板展开;执行期的计算。

C/C++的预处理器能力差意思;C还没模板。所以C的非执行期自动处理就会差意思。呵呵。

hdd961140543 发表于 2013-10-12 08:02:30

Gorgon_Meducer 发表于 2013-10-11 20:46 static/image/common/back.gif
效率上……我主持开发的库——这个可以做信用担保不?

当然可以做担保了!{:smile:}

四轴飞行器 发表于 2013-10-12 08:21:05

顶起   哈哈

滴答滴答下雨啦 发表于 2013-10-12 10:25:44

看不懂,
帮顶算不算支持

Gorgon_Meducer 发表于 2013-10-12 10:28:40

dr2001 发表于 2013-10-12 07:37 static/image/common/back.gif
对于SPI的init, open, close的具体意义,由于涉及到具体芯片的外设配置以及IO功能分配,不好定论。
我对 ...

关于几个方法的作用,按照我对较多(样本数>100,涵盖学生,1~3年工程师,3年以上工程师,教师)用户的调查
大家的观点进行综合以后的结论基本上是——符合一般人简单思维的就好,这个简单思维就是:
1、用之前有一个方法能配置下
2、用的时候就Open下,用完了Close()
3、有一个方法随时能知道当前外设是否在工作,于是提供了Idle()——这是一个状态返回函数。
4、除了Init(), Open(), Close() 以外,根据实际情况提供简单直接的外设操作函数。

一般人不会考虑到状态那么细的地方,我们要做的就是通过封装的方法把所有可能的情况都覆盖到,并封装到库里面
对用户透明,让用户做到随时可以Open(),随时可以Close(),极端情况下甚至要处理以下的情形:

1、比如用户没有Init就Open了——用一个默认的配置来运行——比如USART,用户没有Init就Open了,那么用默认的
9600,n,8,1来工作之类的
2、用户Close的时候,系统要不要采用异步模式,即自动等待正在进行的工作完成后才关闭;当然我觉得可能要提供
一个参数给用户选择——要么无条件立即关闭,要么异步关闭。

抽象的接口,简单的使用逻辑,这是我们追求的目标

Gorgon_Meducer 发表于 2013-10-12 10:31:04

滴答滴答下雨啦 发表于 2013-10-12 10:25 static/image/common/back.gif
看不懂,
帮顶算不算支持

非常感谢,再说说感想嘛,无责任评论~

catwill 发表于 2013-10-12 10:37:19

Gorgon_Meducer 发表于 2013-10-11 20:51 static/image/common/back.gif
Something big is incoming...

这个是一大波僵尸正在接近的意思吗{:lol:}

Gorgon_Meducer 发表于 2013-10-12 10:40:09

catwill 发表于 2013-10-12 10:37 static/image/common/back.gif
这个是一大波僵尸正在接近的意思吗

是不是 大波 我就不知道了……

68336016 发表于 2013-10-12 10:40:20

看不懂也看不下去,感觉就是文言文,看一半就头大了

Gorgon_Meducer 发表于 2013-10-12 10:59:04

68336016 发表于 2013-10-12 10:40 static/image/common/back.gif
看不懂也看不下去,感觉就是文言文,看一半就头大了

谢谢你的反馈。

你能能说说第一个让你就疑惑的东西?或者第一个就让你觉得看不懂的东西是什么?这会对我们很有帮助,谢谢先。

68336016 发表于 2013-10-12 11:29:10

Gorgon_Meducer 发表于 2013-10-12 10:59 static/image/common/back.gif
谢谢你的反馈。

你能能说说第一个让你就疑惑的东西?或者第一个就让你觉得看不懂的东西是什么?这会对我 ...

特地认真看了一片,基本上还是明白的,只不过看到很多宏,同时对其中的一些定义并不很清楚,所以初次见面让人觉得有些晦涩以至于抗拒心理。
不过话说回来,只要了解了其中宏的定义,其它好像也没什么了,用上几次也就习惯成自然了。

Gorgon_Meducer 发表于 2013-10-12 12:29:46

本帖最后由 Gorgon_Meducer 于 2013-10-12 12:31 编辑

68336016 发表于 2013-10-12 11:29 static/image/common/back.gif
特地认真看了一片,基本上还是明白的,只不过看到很多宏,同时对其中的一些定义并不很清楚,所以初次见面 ...

这其实也说明一个问题,为了减小这个 ”首次接触“ 的门槛,我需要在文档或者其他方面加入必要的辅助,
我计划在网站上加入Wizard形式的图文教程,像PPT一样分内容一次一点地介绍使用方法,另外代码生成器
也应该要加入类似现在iOS应用每次更新以后都会在首次打开时加入的那种新特性介绍风格的教程。

catwill 发表于 2013-10-12 14:33:22

看了一遍,还是比较清新的;除了26 和9 比较神话

learner123 发表于 2013-10-12 16:33:07

首先,本人菜鸟,直觉,以下为直觉
1.这是个简单的SPI设备的驱动么?太复杂,这是对我这类门外汉的直觉
2.怎么看都像是用C来搞C++的工作,这是要在x86的工作站上搞类似freeBSD,linux的节奏么?
3.有种VB和java的味道

Gorgon_Meducer 发表于 2013-10-12 16:56:35

learner123 发表于 2013-10-12 16:33 static/image/common/back.gif
首先,本人菜鸟,直觉,以下为直觉
1.这是个简单的SPI设备的驱动么?太复杂,这是对我这类门外汉的直觉
2. ...

谢谢您的反馈,能说明下您是怎样的“门外汉”么?因为有小马过河的故事,
所以,我希望能了解您的视角。当您说复杂的时候,我们就要对这个反馈
做比较深入的分析,并针对结论做相应的调整。

Gorgon_Meducer 发表于 2013-10-12 16:58:25

catwill 发表于 2013-10-12 14:33 static/image/common/back.gif
看了一遍,还是比较清新的;除了26 和9 比较神话

26和9的确比较奇葩,我一定会解决这个问题。

kalo425 发表于 2013-10-12 19:05:00

感想就是代码风格不喜欢····,当然,调用接口,我管你怎么实现的呢···我举手,我有疑问···我是一个学生,还有2年毕业,当看你们讨论的好高深啊····需要看那些书,有哪些实践?就比如状态机吧,就是一味的编写裸机例程,反复实践那些模块就ok?好吧,我跑题了····

whatcanitbe 发表于 2013-10-12 19:35:58

针对一个例子,把当前风格跟一般人使用的风格做个比较

这样会比较容易懂

enovo2468 发表于 2013-10-12 20:00:32

Gorgon_Meducer 发表于 2013-10-12 10:28 static/image/common/back.gif
关于几个方法的作用,按照我对较多(样本数>100,涵盖学生,1~3年工程师,3年以上工程师,教师)用户的调 ...

全部都这样搞就好用多了

Gorgon_Meducer 发表于 2013-10-12 20:43:55

kalo425 发表于 2013-10-12 19:05 static/image/common/back.gif
感想就是代码风格不喜欢····,当然,调用接口,我管你怎么实现的呢···我举手,我有疑问···我是一 ...

看一楼的spi.h不知道是否有帮助

Gorgon_Meducer 发表于 2013-10-12 20:45:46

whatcanitbe 发表于 2013-10-12 19:35 static/image/common/back.gif
针对一个例子,把当前风格跟一般人使用的风格做个比较

这样会比较容易懂 ...

这个…可以看看STM32的对比例子。当然,如果有哪位好心的大哥能
写一个STM32的对比代码就感激不尽了。

SNOOKER 发表于 2013-10-12 22:14:47

不同CPU的IO㡀办法完全使用同一函数来配置吧?

SNOOKER 发表于 2013-10-12 23:01:19

本帖最后由 SNOOKER 于 2013-10-12 23:03 编辑

这是什么语言写的,为什么语法我都看不懂

Gorgon_Meducer 发表于 2013-10-13 00:06:22

SNOOKER 发表于 2013-10-12 23:01 static/image/common/back.gif
这是什么语言写的,为什么语法我都看不懂

C语言,用了一些宏而已

qufuta 发表于 2013-10-13 00:08:08

Gorgon_Meducer 发表于 2013-10-12 16:58 static/image/common/back.gif
26和9的确比较奇葩,我一定会解决这个问题。

对于这个26和9,我们是不是可以用宏在包裹一下,然后这个宏的名字是明确这个数字的含义,感觉这样可能理解起来好些

eduhf_123 发表于 2013-10-13 00:12:33

qufuta 发表于 2013-10-13 00:08 static/image/common/back.gif
对于这个26和9,我们是不是可以用宏在包裹一下,然后这个宏的名字是明确这个数字的含义,感觉这样可能理解起来好些

这两个数字,对不同的应用来讲是各不相同的、不确定的,它们取决于具体应用工程的时钟配置情况以及用户对SPI速度的要求。

具体来讲,这两个数字是根据用户的需求计算出来的。

eduhf_123 发表于 2013-10-13 00:22:03

Gorgon_Meducer 发表于 2013-10-12 16:58 static/image/common/back.gif
26和9的确比较奇葩,我一定会解决这个问题。

PBCLK(外设总线时钟?)、CPSDVSR(“26”)、CLOCKRATE(“9”)、SPI_BAUDRATE之间的关系是怎么定义的?计算公式可以给出来么?

STM32_Study 发表于 2013-10-13 02:19:34

Gorgon_Meducer,不知道你注意到了没有,其实一直以来,包括之前AVR的FW,为什么叫好不叫座?

其实大家吐槽的不是架构,而是一些没有必要的宏定义,你一直以来始终坚持采用宏定义来简化一些结构性的代码,这个是他人很不能适应的东西。如果要用这套东西,那就会跟 dr2001 说的一样,需要付出一些额外的,没有必要的学习适应成本。

比如现在这套框架里面,IO_CFG 就是一个具有代表性的宏。初次接触这类宏的初学者,第一体验是,这是什么啊?然后去看定义,哦,原来是宏啊,代表

#define IO_CFGdo {\
                  io_cfg_t tCFG[] = {

然后,下次遇到的时候,脑子里还要再次翻译一下,甚至一下子记不清了,又得重新确认一下。这里花费的学习成本和时间成本就很高了。而且采用这样的宏,并不能带来其他方面的好处,为什么不采用标准常见的 C 语言的表达方式呢?

另外还有一个很重要的因素,这类宏,在一些辅助阅读和编码的软件上,并不能很好的兼容(比如SourceInsight、SlickEdit)。

我觉得,宏定义的使用,前提是要能够带来一定的效应。代码的可读性是至关重要的,为了写代码的时候省敲几下键盘而牺牲可读性是非常不可取的。

{:lol:}{:lol:}

STM32_Study 发表于 2013-10-13 02:36:19

一个芯片的官方库,如果是公开代码的,其实不仅仅是承载了作为使用库的用途,还是用户使用芯片的一个高效的参考范例库。

比如STM32的库,并不完美,也有很多用户吐槽,但ST的M3市场占有率就说明了这套库是成功的。
因为它不但具备了对外设完善的操作,同时也是一个有效的参考。

这个参考高效的前提,就是采用标准通用的 C 语言风格用法写的。用户阅读的时候,望文达意。我用了STM32那么久,做了十几个项目,基本上的外设都用过了。但还没有完整阅读过Datasheet。根本用不上啊,需要用哪个外设,先看官方范例,再看库里面的操作和注释,然后自己调试调试就好了。这个也是我一直紧跟ST阵营的原因

其他芯片或许有其他方面的一些优势,但如果可以选择,我还是会选择STM32,因为同样不熟悉的情况下,他的学习成本是最低的。

STM32_Study 发表于 2013-10-13 02:43:46

{:titter:}{:titter:}
如果坚持采用IO_CFG   这样风格的宏定义,可以预见,将来这个库的FAQ榜上

请问 IO_CFG 是什么意思啊?

这类问题将遥遥领先,并持久不衰……

takashiki 发表于 2013-10-13 08:44:43

1、说实在的,我非常讨厌不带;结尾的语句,某非是强迫症?即使是Delphi或Javascript,每句结束我都加分号(Javascript可以不要分号,Delphi的end前一句可以不要分号)。你还弄一个宏END_XXX后面居然没有分号,我就会认为这个语句没有结束。
2、我一直认为,用C模拟C++,根本就是歧途,还不如直接用C++呢。问个问题:C如何模拟重载、模板、编译期计算、RTTI?就算模拟成功了,代码和效率都要打NNNN多折扣。我觉得C语言中,SPI1.Open()和Open(&SPI1)相比,没有任何优势,只有劣势:更占CPU、更占内存、更占ROM。但是,C++则没有这些开销,那C模拟的还有什么优势?

STM32_Study 发表于 2013-10-13 09:22:15

takashiki 发表于 2013-10-13 08:44 static/image/common/back.gif
1、说实在的,我非常讨厌不带;结尾的语句,某非是强迫症?即使是Delphi或Javascript,每句结束我都加分号( ...

严重赞同第一点,这些地方采用宏,得不到什么好处,反而要付出很大的代价

第二点,我觉得是代码风格的问题,可以接受 。实际代码,SPI.Open()   和Open(&SPI) 比较,不一定是哪一个就更高效的,要看具体的实现。

而SPI.Open() 这个是所有外设具体的统一的操作,那是一个非常好的方式,不用再去查找相关函数了。所有外设都可以这样开关。
如果要用Open(&SPI)来实现,那这个函数里面要判断所有的外设,反而更不高效。

myxiaonia 发表于 2013-10-13 10:01:15

本帖最后由 myxiaonia 于 2013-10-13 10:02 编辑

dr2001 发表于 2013-10-11 22:23 static/image/common/back.gif
供参考:
1、首要的,这是C代码片段。所以,对于块结构的封装最好能提供花括号对{}。引入其它语言的块结构 ...

你所说的列式 io配置 ,是否就是寄存器的列式配置,我好像就是这么做的
这是我的一个spi配置函数void SPI1_Init(void)
{
        RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;                               /* enable SPI1 clock               */
        SPI1->CR2   = 0
                                        |SPI_CR2_RXDMAEN
                                        |SPI_CR2_TXDMAEN
//                                         |SPI_CR2_SSOE
//                                         |SPI_CR2_ERRIE
//                                         |SPI_CR2_RXNEIE
//                                         |SPI_CR2_TXEIE
                                        ;
        SPI1->CR1          = 0
//                                         SPI_CR1_CPHA|SPI_CR1_CPOL                        //模式0
                                        |SPI_CR1_MSTR                                                //主模式
//                                         |SPI_CR1_BR_0                                                //波特率分频器                128分频(0.5MHz)
                                        |SPI_CR1_BR_1
                                        |SPI_CR1_BR_2
                                        |SPI_CR1_SPE                                                //SPI使能
//                                         |SPI_CR1_LSBFIRST                                       
                                        |SPI_CR1_SSI                                                //NSS软件模式,必须保证SPI为主模式
                                        |SPI_CR1_SSM                                                //NSS软件模式
//                                         |SPI_CR1_RXONLY
//                                         |SPI_CR1_DFF                                                //8bit模式
//                                         |SPI_CR1_CRCNEXT
//                                         |SPI_CR1_CRCEN
//                                         |SPI_CR1_BIDIOE
//                                         |SPI_CR1_BIDIMODE
                                        ;       
        SPI1DMA_Init();
        InitFm25cl64StateReg();
}

myxiaonia 发表于 2013-10-13 10:02:59

myxiaonia 发表于 2013-10-13 10:01 static/image/common/back.gif
你所说的列式 io配置 ,是否就是寄存器的列式配置,我好像就是这么做的
这是我的一个spi配置函数 ...

对齐的格式怎么显示成这个样子

takashiki 发表于 2013-10-13 10:32:43

STM32_Study 发表于 2013-10-13 09:22 static/image/common/back.gif
严重赞同第一点,这些地方采用宏,得不到什么好处,反而要付出很大的代价

第二点,我觉得是代码风格的问 ...

相比于C++来说,C用结构体的函数指针代替C++的成员函数。C++编译后可以通过直接调用完成,而C必须先初始化该函数指针,然后间接调用,这就浪费了一个函数指针的RAM空间,初始化函数指针以及函数指针调用的ROM空间,以及时间开销。
C主要是面向过程编程,那么直接使用SPI_Open()是不是更加高效?那些所谓的封装,有时候是有些过度了,典型的比如Java;如果真的一定要那么封装,我的意见是还不如直接使用C++。
用C的函数指针模拟C++的虚函数(更变态一点,使用模板,继承都只是静态方法,连虚函数表都不要),开销是必须的,无可避免的。

一般情况下,C++比C的效率低。在某些领域,会比C高,比如iostream::cout比sprintf效率高到不知哪儿去了,因为无需判断参数类型,可以函数或操作符重载。

STM32_Study 发表于 2013-10-13 10:36:19

takashiki 发表于 2013-10-13 10:32 static/image/common/back.gif
相比于C++来说,C用结构体的函数指针代替C++的成员函数。C++编译后可以通过直接调用完成,而C必须先初始 ...

是的。从这个层面来说,采用SPI_Open()是最高效的。使用也方便

Gorgon_Meducer 发表于 2013-10-13 11:29:30

本帖最后由 Gorgon_Meducer 于 2013-10-13 12:29 编辑

STM32_Study 发表于 2013-10-13 10:36 static/image/common/back.gif
是的。从这个层面来说,采用SPI_Open()是最高效的。使用也方便

用SPI.Open(&SPI1)和 SPI1.Open()
在效率上是一样的
因为在内部代码实现上
SPI1.Open()是一个函数对SPI.Open(&SPI1)的二次封装,也就是说不存在你说的问题。
你可以这么考虑,
1、当你要使用SPI.Open(&SPI1)的时候,你需要写这句话,而写这句话的时候,就会产生对应的代码
2、如果你很多地方都要写SPI.Open(&SPI1),那么编译器可能会产生多个副本——聪明的编译器则可能产生一个副本
3、如果你直接提供一个SPI1.Open() 那么永远只有一个副本,无论你用多少次,编译器都只产生一个副本,并在需要
    用的时候对这个副本产生函数调用,这就达到了效率最优化。
4、聪明的编译器会对不使用的公共成员进行优化,举例来说,你从来没有用过SPI2.Open()编译器会自动将其优化掉。
5、一般在C里面,产生类实例都是在编译时刻——怎么说呢,就是实际上这些接口开销的只是ROM,并不消耗RAM。
    SPI1这个结构体(或者说是接口)是唯一保存在ROM里面的,在编译时刻得到唯一的初始化——用具体的函数初始化
    函数指针——这就是静态实例。
6、如果SPI这个结构体是在编译时刻其内容就已经确定了的话,比如SPI是常量,那么SPI.Open()和SPI_Open()编译器
    产生的代码是一样的——对编译器来说,但凡在其编译时刻可以确定的内容,它都会尽可能优化的。所以如果一个
    对象是常量实例,则其成员函数的调用和普通函数的调用产生的代码是一样的,不会出现通过指针间接寻址的情况。

另外回答一下用C实现面向对象的意义:
1、用C实现面向对象绝对不是为了模拟C++,模拟C++绝对不如直接用C++
2、用C实现面向对象是为了在需要的时候,仅仅使用某些你特别需要的面向对象的特性,而不必使用整个面向对象的
    语言,比如C++。举例来说,你希望获得面向接口的开发形式,你完全可以使用结构体来定义函数指针的的表,
    从而获得接口,在我后面会展示的软构件技术时候,你们会感受到这一点。
3、C有时候需要使用覆盖,这种情况下,就不得不在类的成员中加入一个虚函数——也就是一个函数指针,继承该类
   的派生类只要修改这个函数指针的值就可以完成覆盖。很多时候我们需要这样的特性——仅仅是某些小的部分,同时
   我们也需要C语言的效率,这种根据需要慎重模拟一些面向对象技术的方式才是最经济的。重载在C语言中是通过
   可变参数列表来实现的,效率不高,一般不推荐使用。
4、切记不可以为了面向对象的形式而做不必要的结构,比如虽然我们完全有能力为每一个C模拟的类都加入成员方法,
   但只要这些成员方法是不需要重载的,或者不是为了面向接口开发的,就完全没有必要将其函数指针封装到结构体里面。

总结:C拥有效率,面向对象(注意不是C++)拥有开发上的便利,我们可以在需要的时候通过模拟一些面向对象的特性
         来获得所需的便利,同时在其它部分又不会损失C的效率。一切为了应用和实现,而不是为了格式和形式。

请永远铭记:面向对象只是一种思想,不是一种或几种语言的专利,你可以在你需要的时候使用对应的思想,按需分配
         灵活设计你的代码而不被具体思想或者方法论束缚,才是程序员需要掌握的技能——因为程序员是思维的固化者,
         必须要有灵活屈驾各种思维模式的能力。总是标榜自己是坚定的汇编主意者,坚定的C面向过程的,或者坚定的
      面向对象者,都是完全没有必要的,这不是萌战,这是一个白猫黑猫抓到老鼠就是好猫的工程之道。

也许大家看了我的代码认为我在格式上花费了太多精力,其实没有,我是完全面向效率和应用的,格式只是一个个人风格
问题,我不会为了格式而牺牲效率——除非这个格式带来巨大的好处——比如可读性非常好,通用性非常好——对于这种
情况,我倾向于产品的第一个prototype用这个格式来获得开发效率,而在第二阶段对相应的部分进行最直接的优化,以
换取效率。

希望这些对大家有所帮助。

Gorgon_Meducer 发表于 2013-10-13 11:46:07

myxiaonia 发表于 2013-10-13 10:01 static/image/common/back.gif
你所说的列式 io配置 ,是否就是寄存器的列式配置,我好像就是这么做的
这是我的一个spi配置函数 ...

这就是一个很好的比较,这种寄存器的列式配置本身已经很清晰了,但是对于没有阅读DS的人来说
宏的意义还不够明确——在这里宏的作用就是当 一个数字不代表其意义本身的时候,为其追加一个
恰当的说明——所以,应该对这种配置进行必要的封装,以获得最大的可读性,同时又不会丧失对应
的效率,恰当的设计枚举以及宏,配合少量的代码是完全可以做到 一定范围内,通用性和效率兼得的。

takashiki 发表于 2013-10-13 11:46:49

Gorgon_Meducer 发表于 2013-10-13 11:29 static/image/common/back.gif
3、C有时候需要使用重载,这种情况下,就不得不在类的成员中加入一个虚函数——也就是一个函数指针,继承该类
   的派生类只要修改这个函数指针的值就可以完成重载。很多时候我们需要这样的特性——仅仅是某些小的部分,同时
   我们也需要C语言的效率,这种根据需要慎重模拟一些面向对象技术的方式才是最经济的。

C实现重载?不可能吧,C99还差不多~~~~
作为一个专家级人物,您首先还是应该先区分重载(overload)和覆盖(override)的区别吧……

Gorgon_Meducer 发表于 2013-10-13 11:48:03

takashiki 发表于 2013-10-13 08:44 static/image/common/back.gif
1、说实在的,我非常讨厌不带;结尾的语句,某非是强迫症?即使是Delphi或Javascript,每句结束我都加分号( ...

对于第一条,这是一个风格问题,其实上面的代码完全可以追加一个;不影响。不存在效率上的差异和损失。

Gorgon_Meducer 发表于 2013-10-13 11:49:31

本帖最后由 Gorgon_Meducer 于 2013-10-13 11:53 编辑

takashiki 发表于 2013-10-13 11:46 static/image/common/back.gif
C实现重载?不可能吧,C99还差不多~~~~
作为一个专家级人物,您首先还是应该先区分重载(overload)和覆盖( ...

这里是我说错了,是Override,你说的很对。我修正。overload在C里面是通过可变参数列表来实现的,效率不高。
另外,请修正一下,我不是什么专家级的人物——不要把我和这个词扯上什么关系——我只是一个老程序员。

Gorgon_Meducer 发表于 2013-10-13 11:58:58

本帖最后由 Gorgon_Meducer 于 2013-10-13 12:00 编辑

为了解释一些OOC的技术实现问题,在1楼更新的spi.c的完整代码,还请大家更猛烈的拍砖。
特别说明,该代码还未通过最后一轮效率优化,理论上必然存在可以优化的地方,如果您发现了
请务必告诉我,非常感谢!

takashiki 发表于 2013-10-13 12:00:33

Gorgon_Meducer 发表于 2013-10-13 11:48 static/image/common/back.gif
对于第一条,这是一个风格问题,其实上面的代码完全可以追加一个;不影响。不存在效率上的差异和损失。 ...

是的,我没有说存在影响,只是我会觉得这个语句没有结束,反而会引起误解。因为我一直认为,程序是给人看的,人都有误解了,这样的风给就是不好的。

另外,针对您所说的SPI.Open()和SPI_Open()效率一致的问题,我还从没有发现过这么牛X的编译器出现过。如果出现了,只能是这个编译器有BUG。
举例(这里忽略了继承派生): 例子1:面向过程的SPI_Open

typedef struct _SPI_STRUCT { int XXX; int YYY; } SPI_STRUCT;
void SPI_Open(SPI_STRUCT* pSPI){...}例子2:面向对象的C版本的SPI.Open
typedef struct _SPI_STRUCT{
    int XXX;
    int YYY;
    void (*Open)();
}SPI_STRUCT;

SPI_STRUCT SPI;
SPI.Open = xxx;
SPI.Open();例子3:面向对象的C版本的SPI.Open
struct SPI_STRUCT{
    int XXX;
    int YYY;
    void Open(){
    }
}; 就以上面三段来说,分别对结构体(或类)SPI_STRUCT取sizeof,一样吗?很显然例子2中有RAM浪费了,SPI.Open = xxx; 这一句ROM浪费了。C++如果采用虚函数,会有虚函数表的开销;如果采用模板,则这点开销都没有,C根本无法模拟的。

Gorgon_Meducer 发表于 2013-10-13 12:02:51

本帖最后由 Gorgon_Meducer 于 2013-10-13 12:14 编辑

takashiki 发表于 2013-10-13 12:00 static/image/common/back.gif
是的,我没有说存在影响,只是我会觉得这个语句没有结束,反而会引起误解。因为我一直认为,程序是给人看 ...

注意,我说的是静态实例,不要断章取义,也就是说SPI这个结构体是常量,在ROM中的,只消耗ROM,不额外消耗RAM。
请看1楼的实现代码。另外,我说SPI_Open和SPI.Open一样是指在静态实例的情况下,调用的时候,生成的用于调用的
代码是一致的。

在我这里,C从来都不是要模拟C++,也不是要取代C++,C有自己的劣势,这是必然的。但我想说的只是在需要的时候C可以
通过一些手段获得一些面向对象的便利,至于形势往往并不重要。如果继续讨论有些东西只有C++可以实现,C不可以实现,
我很明确的答复你——完全同意,严重同意——这是必然的嘛,不然C++作为一个独立的语言存在就太不给力了。

我们讨论的语境是,C是否可以在需要的时候模拟一些面向对象的技术——同时是否可以减小额外的开销——而不是不开销。

我们用函数指针,显然必须开销指针所需的存储器,32位系统里面是4个字节。但开销了必须要有意义,比如获得了面向
接口开发的便利——而且是面向动态接口的便利,如果是运行的程序所依赖的接口根本不会变,那么消耗这个函数指针也
完全没有必要——或者消耗这个函数指针是为了覆盖——如果你要这些功能,对C这种原始的语言来说, 这些开销就是必
须的,这就不算浪费——是代价。所谓浪费就是,完全没有必要的开销——如果在必须要用C的环境下,实现某个功能有
更经济的做法,那么当前做法产生的开销可能就是浪费。

yiming988 发表于 2013-10-13 12:04:02

Gorgon_Meducer 发表于 2013-10-13 11:48 static/image/common/back.gif
对于第一条,这是一个风格问题,其实上面的代码完全可以追加一个;不影响。不存在效率上的差异和损失。 ...

对于takashiki网友说的第一条,我觉得蛮有道理的; 是否可以稍微改变一下IO_CFG和SPI_CFG的封装方法,让它看起来更符合我们C语言的使用习惯 :)

Gorgon_Meducer 发表于 2013-10-13 12:05:28

yiming988 发表于 2013-10-13 12:04 static/image/common/back.gif
对于takashiki网友说的第一条,我觉得蛮有道理的; 是否可以稍微改变一下IO_CFG和SPI_CFG的封装方法,让 ...

可以,能否更具体的推荐一种呢?因为我可能存在思维上的束缚——毕竟要推翻自己提出的方案,不借助外力
还是很有难度的。

takashiki 发表于 2013-10-13 12:18:31

Gorgon_Meducer 发表于 2013-10-13 12:02 static/image/common/back.gif
注意,我说的是静态实例,不要断章取义,也就是说SPI这个结构体是常量,在ROM中的,只消耗ROM,不额外消 ...

我不知道是我逻辑混乱还是怎么的,静态实例 = 结构体是常量??? 这么个静态实例真没见过。常量的话,确实是没有RAM、效率的损失,只是ROM稍微多了那么几个字节,但是带来的后果就是结构体内容无法修改,也许你需要的就是不需要修改。如果是这样,我认为Keil的初始化配置文件非常好,非常实用,目前没有比他更好用的东西出现。

Gorgon_Meducer 发表于 2013-10-13 12:23:32

本帖最后由 Gorgon_Meducer 于 2013-10-13 12:26 编辑

takashiki 发表于 2013-10-13 12:18 static/image/common/back.gif
我不知道是我逻辑混乱还是怎么的,静态实例 = 结构体是常量??? 这么个静态实例真没见过。常量的话,确实 ...

可能是在表述不准确,造成误解,对不起……
我一般把 静态实例 解释为编译时刻就已经确定其内容的实例,不知道我这样说明是否会和 静态实例 的本来意义产生冲突。
我并不太熟悉面向对象语言的 术语系统, 我通常只用,有时候比较懒,没有仔细看就根据自己的主观判断乱用术语,这是
真的,所以如果这里用错了,请务必告诉我。非常感谢。

即便是这样,你提醒了我,这里用静态实例描述似乎的确不够稳妥,因为静态实例还有可能是变量,这里一定要加入常量的
修饰,否则不满足我前面的说法。我就耍下赖——你看我代码嘛……我的代码是这么写的……

OK,错了就是错了,我马上修正。再次感谢。

Gorgon_Meducer 发表于 2013-10-13 12:32:08

本帖最后由 Gorgon_Meducer 于 2013-10-13 12:36 编辑

yiming988 发表于 2013-10-13 12:04 static/image/common/back.gif
对于takashiki网友说的第一条,我觉得蛮有道理的; 是否可以稍微改变一下IO_CFG和SPI_CFG的封装方法,让 ...

修改为下面这种形式如何呢?
    IO_CFG() {
      {PA1, IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA1, IO_WORKS_AS_SPI1_MOSI & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA2, IO_WORKS_AS_SPI0_SCK& IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA2, IO_WORKS_AS_SPI1_SCK& IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA3, IO_WORKS_AS_SPI0_CS   & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA3, IO_WORKS_AS_SPI1_CS   & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA4, IO_WORKS_AS_SPI0_MISO & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA4, IO_WORKS_AS_SPI1_MISO & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
    } END_IO_CFG();
或者这个形势呢?
    IO_CFG(
      {PA1, IO_WORKS_AS_SPI0_MOSI & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA1, IO_WORKS_AS_SPI1_MOSI & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA2, IO_WORKS_AS_SPI0_SCK& IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA2, IO_WORKS_AS_SPI1_SCK& IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA3, IO_WORKS_AS_SPI0_CS   & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA3, IO_WORKS_AS_SPI1_CS   & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},

      {PA4, IO_WORKS_AS_SPI0_MISO & IO_READ_ONLY,    IO_PULL_UP | IO_RAW_LOOP_BACK},
      {PA4, IO_WORKS_AS_SPI1_MISO & IO_WRITE_ONLY,   IO_PULL_UP | IO_RAW_LOOP_BACK},
    );

bbsview 发表于 2013-10-13 12:43:00

目前还没用到那么好的结构,不过以后肯定会用到,收藏

Gorgon_Meducer 发表于 2013-10-13 12:51:11

bbsview 发表于 2013-10-13 12:43 static/image/common/back.gif
目前还没用到那么好的结构,不过以后肯定会用到,收藏

多吐吐槽阿,你没看很多人都吐得很开心么?

kinsno 发表于 2013-10-13 13:06:52

Gorgon_Meducer 发表于 2013-10-12 10:28 static/image/common/back.gif
关于几个方法的作用,按照我对较多(样本数>100,涵盖学生,1~3年工程师,3年以上工程师,教师)用户的调 ...

又省了我一些步骤,正在做这方面的工作,封装了一些函数;
不过有待提高,因为是在项目工程里,所以不太适合摘出来了.
且看,版主下面更好的封装!

Gorgon_Meducer 发表于 2013-10-13 13:09:14

本帖最后由 Gorgon_Meducer 于 2013-10-13 13:10 编辑

kinsno 发表于 2013-10-13 13:06 static/image/common/back.gif
又省了我一些步骤,正在做这方面的工作,封装了一些函数;
不过有待提高,因为是在项目工程里,所以不太适合摘 ...

不必摘抄代码出来,但是可以交流下具体的思想,方法和技巧阿。
常言道,无舍无得。你看我现在,就是拿出自己的东西给别人批判,才能获得更多的意见反馈,
才能避免闭门造车。如果一味借鉴,那么可能就会造就别人的那句话:
“一直被模仿,从未被超越”——开个严肃的玩笑,不要多想。

kinsno 发表于 2013-10-13 13:10:05

Gorgon_Meducer 发表于 2013-10-12 12:29 static/image/common/back.gif
这其实也说明一个问题,为了减小这个 ”首次接触“ 的门槛,我需要在文档或者其他方面加入必要的辅助,
...

其实我猜测很多人抗距你这个,或者首次接触的心理抵触,可能就是你最喜欢用的宏上面;

宏,对于新手或一般不爱用宏的人来说,确实是一个很头疼的事情,他们可能更喜欢看到很直观的代码罢了!

Gorgon_Meducer 发表于 2013-10-13 13:12:41

kinsno 发表于 2013-10-13 13:10 static/image/common/back.gif
其实我猜测很多人抗距你这个,或者首次接触的心理抵触,可能就是你最喜欢用的宏上面;

宏,对于新手或一般不 ...

对抗这一点,我很有心得:
1、绝对不推广——好东西要别人自己来用
2、保持宏一定要好用,能偷懒,傻瓜才不用
3、保持规则一致且简单,既然为了偷懒,当然简单好记,规则统一才行
4、终极解决目标——代码生成器——如果代码都能替你生成了,鬼才管你用了多少宏呢

我现在就在准备第四步骤。

yiming988 发表于 2013-10-13 13:15:13

Gorgon_Meducer 发表于 2013-10-13 12:32 static/image/common/back.gif
修改为下面这种形式如何呢?或者这个形势呢?

我的话,更喜欢后者的格式,使用不会感觉别扭,阅读也一眼可以看出作用域

kinsno 发表于 2013-10-13 13:15:18

Gorgon_Meducer 发表于 2013-10-13 12:32 static/image/common/back.gif
修改为下面这种形式如何呢?或者这个形势呢?

有一个疑惑?

你这个是你将来要开发某个芯片的库,这样来做呢?还是在你的书里或将来倡导大家以这种格式来写代码,如果是以这种格式来写,我觉得你倒不如提供一些函数接口给一些人,比如你说的OPEN,CLOSE INIT等,把形参定义好也不错啊; 如果说你要开发某个库,或将来你打算按照这种模块格式来做,我觉得......可能更参考一些1-3年或大四实验室的人来说会更好一些,因为他们更具有可塑性,而对于一个成年码码的人来说,可能会难,因为有些风格一旦定了,让他纠正过来,代价有点大,尽管你这个更方便一些,但人都会有习惯性的啊.

我还是没搞明白,你倡导这个的目的在哪?

kinsno 发表于 2013-10-13 13:16:27

Gorgon_Meducer 发表于 2013-10-13 13:12 static/image/common/back.gif
对抗这一点,我很有心得:
1、绝对不推广——好东西要别人自己来用
2、保持宏一定要好用,能偷懒,傻瓜才 ...

厉害,第4点好,我上面有一个贴子刚问你的目的,这个可不可以说就是你本贴的目的,其实你的目的是要为某个芯片出代码生成器呢? 哈哈. 真是福音啊!

Gorgon_Meducer 发表于 2013-10-13 13:16:50

kinsno 发表于 2013-10-13 13:10 static/image/common/back.gif
其实我猜测很多人抗距你这个,或者首次接触的心理抵触,可能就是你最喜欢用的宏上面;

宏,对于新手或一般不 ...

我,今天,就及其不负责任的发泄一下情绪:
“一,愿天下所有不爱用宏的人,天天遇到大型项目”
“二,愿天下所有爱展开宏的人,天天遇到高度封装的Framework”
“三,愿天下所有不放心使用黑盒子的人,写的代码天天被别人怀疑”

最后:
“愿天下所有 不看到宏展开会死星人,一辈子被以上三条困惑”

Gorgon_Meducer 发表于 2013-10-13 13:18:14

本帖最后由 Gorgon_Meducer 于 2013-10-13 13:22 编辑

kinsno 发表于 2013-10-13 13:15 static/image/common/back.gif
有一个疑惑?

你这个是你将来要开发某个芯片的库,这样来做呢?还是在你的书里或将来倡导大家以这种格式 ...

不是将来要开发一个芯片——我只能说这么多

书中会介绍对应的技术,但不推荐使用,因为一个编码风格和一个编码规范只是一个思想的产品,我要传达的是思想本身
而不是思想的产品。

另外,我不是倡导……我是希望借助大家的意见来修改我的编码风格,我没有倡导,只是交流。

Gorgon_Meducer 发表于 2013-10-13 13:19:04

yiming988 发表于 2013-10-13 13:15 static/image/common/back.gif
我的话,更喜欢后者的格式,使用不会感觉别扭,阅读也一眼可以看出作用域 ...

谢谢!明白!其它人的看法呢?

Gorgon_Meducer 发表于 2013-10-13 13:21:25

kinsno 发表于 2013-10-13 13:16 static/image/common/back.gif
厉害,第4点好,我上面有一个贴子刚问你的目的,这个可不可以说就是你本贴的目的,其实你的目的是要为某个芯 ...

是的……代码生成器……必须有……这是公司项目

learner123 发表于 2013-10-13 14:47:36


首先是猜测:
1。从实例上看不是极度复杂的soc系统,猜测是类似单片机规模的或者手机芯片,也有可能是设备芯片。应该不会是X86,POWERPC,MIPS通用处理器的外围扩展芯片组。
2。从软件上看,目前没有看到MMU,不排除是类bsd和linux的较为复杂的系统。
3。从楼主的表述看,应该是搞驱动测试,或者所谓软件“平台策略”
因为不知楼主吃饭的家伙到底牛叉到何种地步,目标是建立多么庞大的软件/硬件帝国,所以从一般中低端嵌入式开发者角度评价(就是洗衣机,电磁炉,工业自动化的小作坊开发公司的软件民工的角度):
1.既然是小系统,一般开发公司都有自己的技术积累,有些开发是将已有的软件移植升级而已。大部分中小公司都有成型的软件规范或开发者自己独特的风格。大部分都不会主动接受楼主的软件风格的。所谓可读性,主要还是依赖注释及文档,即使是大公司也一样,例如微软。而且,对于驱动来说,硬件资源通常还是要结合数据手册,寄存器图示方式较为通俗易懂,因为调试开发通常是硬件人员出身。
2对于应用软件来说,无论哪种操作系统对于用户来说还不至于关注寄存器,驱动由设备商写好,调用即可。所以应用软件开发通常不关心楼主这部分代码
3对于硬件开发测试来说,恐怕关心的是数据通路和寄存操作,并不是写应用层相关的驱动程序,只是测试程序而已
4若是自动测试,或者自动代码生成器,恐怕就没有必要关心一般用户的可读性。而且,楼主这个芯片还不至于像16c550这么牛x和经久,还不至于驱动程序都是自动代码生成。
5对于类似低成本的消费类芯片,比如洗衣机用的单片机系统,有一大批读汇编和看寄存器的民工,不用为他们的生计担心。

其实,楼主与其建立一个庞大的软件帝国,不如将文档写的清晰易懂来的方便。至于自动测试,小芯片搞牛刀就太浪费了,如果是自己公司作为一种企业标准,软件风格没什么说的,只和老板或者技术管理层的性格有关。
至于大项目,大项目也要拆成小项目的,系统庞大,并不意味着所有的开发人员都要关注最底层。软件层次没搞好,接口出问题,测试不到位,管理不到位才是症结。但这些和复杂的宏定义没有关系。

Gorgon_Meducer 发表于 2013-10-13 15:01:09

本帖最后由 Gorgon_Meducer 于 2013-10-13 15:36 编辑

learner123 发表于 2013-10-13 14:47 static/image/common/back.gif
首先是猜测:
1。从实例上看不是极度复杂的soc系统,猜测是类似单片机规模的或者手机芯片,也有可能是设备 ...

你说的很对,但我就是芯片提供商……我不提供这个……谁提供……?
芯片是Cortex M系列的芯片,所以估计一般的洗衣机,小家电之类的是不屑于使用的——尼玛超过1RMB,怎么活?
在ARM芯片上跑汇编,大部分情况下,还是有点吃力不讨好的,没有一个驱动库你都不好意思说你是32位芯片。
-----------------------以下 广告软文,大家洗洗睡吧-----------------------------
另外,从规模上,一个带了10个16位定时器(每个4路PWM输出,输入比较,双斜坡计数模式,可以级联),
5M 12Bit ADC 10M输入阻抗,带差分,带增益,
3个独立AHB Slave的SRAM控制器,16通道DMA……4个USART,3个SPI, 2个I2C, 1个USB Device, 1个I2S的芯片,
外设的数字引脚可以任意分配到任意IO,
从500多个外设信号里面可以任意选择32个出来产生32个独立终端请求,
也可以用多个信号经过逻辑运算(与或非,异或)后产生一个中断请求
可以用任意外设信号触发任意外设的功能,
系统主频最高72M的低功耗芯片……

我据会直接告诉你只要1美金不到么,我会好意思说这个芯片没有官方驱动库么?

P.S. 撇清关系:我和GDxxxx, LGTxxxx,新唐xxxx,木有半毛钱关系,ARM内核授权是买的……
----------------------以上软文,大家洗洗睡吧-------------------------------------

换句话说,STM32的库也有自己的风格,但是你不能说大家自己去看数据手册好了,不用你这个库……
而且风格上,你要注意到,很多时候我只是二次封装,原有的结构还是很基础的,比如你可以不用IO_CFG
直接自己调用IO.Config()函数。

即便是硬件出生的工程师,他们很多时候调试硬件只是为了实现所需的功能才会去看寄存器,如果官方
提供的库本身就能实现这些功能,大家多半不愿意去查看寄存器的基础定义,然后自己写代码,这很大
程度上引入了不可靠因素——因为可能存在对DS的理解偏差,可能存在配置寄存器时候的失误。而提供
库的好处就不多强调了。
例如休眠,这一类功能,实际上很多硬件工程师要花费大量的时间来研读DS,找到正确配置的方法来获取
指定模式下的最小功耗,而实际上,库就应该提供官方调试过的,简化的抽象的接口,直接完成对应的配置
避免用户自己去操作寄存器,避免让用户自己去注意一些由硬件带来的不必要的时序上的关注。用户要的
只是一个很明确很明确的目的——休眠到某个模式,关闭某个东西——我们提供这个就可以了,而寄存器
只是一个媒介,时序是完全多余的东西——必须要把开发人员从这个不必要的东西里面解放出来。

如果我不提供这个,用户就会有意见,大客户我们会直接技术支持,他们什么编码风格,我们都会去调整
去支持,但小客户呢?小客户还是希望官方能提供完善的驱动代码。风格上的问题,既然你要提供驱动给
一部分人,自然要征求他们的意见,所以才有了这个帖子以及后续一系列的帖子。

不能同意你的一些说法,因为有很多东西不存在别人已经有了我就不能有这种逻辑——当然,如果是一种
建议,我觉得建议非常好,值得听取,我们会改进,体现出自己的价值,避免让别人屋上架屋。

我不是要建立软件帝国,我只是要给一款芯片提供配套的库。

很同意你最后一段说的内容,我而要做的就是避免大部分人关注底层,我要做的就是要做好层次,并且帮助
大家管理好层次,做好接口,只有做好了接口,充分有效的测试才是能高效率的进行。

最后,不存在“与其……不如……”的问题,驱动必须要清晰,文档也必须要通俗易懂资料充分,都是必须的!
一步一步来,一个都不能少!

learner123 发表于 2013-10-13 15:59:03

Gorgon_Meducer 发表于 2013-10-13 15:01 static/image/common/back.gif
你说的很对,但我就是芯片提供商……我不提供这个……谁提供……?
换句话说,STM32的库也有自己的风格, ...

本人不是说不用维护芯片驱动函数库,直接看数据手册,只是驱动程序库的关注点问题
st的做法和楼主不同之处在于:没有强制用户接受其设备库的规范。
也就是说,对于用户来说,只关心接口,而不关心你库本身的风格。而楼主的规范直接让最终用户来接受其设备库的编程规范。ST自己的库在接口部分用的基本就是简单的常数宏定义,没有过多花哨的地方。当然有些小技巧,在库内部(功能实现上)使用,接口没有过多的花。此外,ST对设备的功能抽象比过程抽象做的好,楼主只关注了过程抽象,其实更应该关注功能抽象。

有些东西,调查后才有些结论的,我们这边大约有10~20位的从入门到出道的工程师的经历,学历大专、本科、硕士、博士基本全部覆盖,基础参差不齐,目前出现的问题倒不是查看数据手册环境头疼,主要还是在设备功能抽象。至于懒得读手册的问题,看似用库能避免,但调试出现问题后还是回到数据手册了,一些BUG是在寄存器级操作发现的。只有芯片使用入门时,才有所谓"数据手册的寄存器定义综合症“。山寨小公司的1人完成整个项目的开发制度才是根源。

STM32的芯片都有不少bug,除非楼主公司牛x或者芯片规模小,否则我想,肯定也有不少bug。这些bug很多时候必须由应用工程师来发现。很多时候测试性的工作不全是开发芯片的搞出来的。

文档问题远远比一个驱动函数库重要,搞芯片的第一步驱动函数本身的编写反而不是最应该关注的。即使是手机芯片(规模已经不小了),驱动函数的定义也不是像楼主这么个搞法。有些时候,虽然不经常,民工们还是要对驱动函数库做部分修改,和查看其实现的。ST,TI,freescale,Atmel各类单片机都不会搞到这种地步,TI的影子寄存器已经让不少人烦恼,但也绝对没搞到楼主这种地步。

Gorgon_Meducer 发表于 2013-10-13 16:06:59

本帖最后由 Gorgon_Meducer 于 2013-10-13 16:34 编辑

learner123 发表于 2013-10-13 15:59 static/image/common/back.gif
本人不是说不用维护芯片驱动函数库,直接看数据手册,只是驱动程序库的关注点问题
st的做法和楼主不同之 ...

我没有强制别人用我的规范哦,我只是说,这种风格的库大家会有什么想法。如果你仔细看库,你发现,基本的
东西还是基本的,也没有牺牲效率,也完全不必使用我提供的那些宏。
你用的比较多的说法是——没有搞到你这种程度——我想知道是怎样的程度呢?我应该如何改进才能减小这种
程度呢?
另外,从目前的代码来看,我只展示了一个简单的驱动模型,你不可以直接下结论说我不关注功能的抽象哦。我认真调查了
别家的风格,我可以说,在功能的抽象上我比他们走的更远。结束工程师对底层寄存器的访问是一个大趋势,势不可挡,
但即便时代如何发展,需要工程师关注底层寄存器的情况会一直存在的,就像现在32位系统大趋势是不用汇编开发了,但
实际情况是,汇编会长期存在,必须用汇编的场合也永远存在。我们 希望结束 大部分工程师对底层寄存器的访问,这是一种
努力,决不是什么断言,也不是什么不做到就会浑身难受得事情——最终目的还是要节省开发人员的时间。

至于风格上,真的别误解我,我没有强制大家这么做,底层函数都在那里,文档也会写得清清楚楚,我只不过会顺手推销
以下,说,我有一些模板,用宏封装了以后用起来会方便一些,你们喜欢用就用吧,不喜欢直接调用函数也是可以的。

那些枚举阿,本质上就是一个常数,和别家的常数没有什么不同。函数无论用函数指针过度下还是不用函数指针过度下,
还是标准的C语言,而且是ANSI-C,用不用宏模板,完全是自愿的事情。

我们试图节省工程师的时间,如果掌握底层对大家更重要,那么数据手册会弥补这个内容,你也可以直接访问寄存器,如果
你仔细看了驱动,你会发现我们并没有封堵大家访问寄存器的能力,甚至提供了专门的接口:
比如SPI0.ptRegPage->xxxx就可以直接访问对应的寄存器。

不改变用户原有的习惯是非常重要的,但我们在提供了满足原有习惯的接口的同时,推荐一些自己新的东西,也是无可厚非的,
何况无论是愿望,目标还是实际效果也并不都是坏的。


我们很小,所以我们很专注,我们的文档一定也会很专注,这点请相信我——因为每一个文档都会赌上我的名誉——这也是
我这个虚拟身份唯一最珍贵的东西。我不能保证文档完美无缺,但我能保证文档一定会不断完善,符合傻孩子自诩的通俗标准。



上面的讨论让我想起一种说法:在团队外,库只能建议,不能推广;真正的好东西不是你建议别人使用的,而是别人求来使用的
你要做的是在别人来寻找的时候为其准备好所需的所有材料。团队内,直接强制就行了。所以,某些意义上说,同样一个事物,
团队外的效率是无法予以保证的,因为大家并不确保会遵守一些规范;团队内容易实施规范,只要规范本身足够科学,是能确保
效率的,而这种效率以及带来这种效率的规范往往让外面的人有几分羡慕——这就是为什么曾经一度,华为编码规范让大家瞳孔
为之放大,官方又会干预论坛共享化为规范的行为。

以上是我的一点感想,受到本人阅历的限制,如果有不妥的地方,请务必指出。多交流才会有收获。
页: [1] 2
查看完整版本: [交流][拍砖]新版驱动接口风格调查(一):IO配置