The Accel Application

 

The Accel Application

Let's put this newfound knowledge to work by writing an application that scrolls. Accel draws a window that resembles Microsoft Excel. (See Figure 2-13.) The spreadsheet depicted in the window is 26 columns wide and 99 rows high—much too large to be displayed all at once. However, scroll bars allow the user to view all parts of the spreadsheet. In addition to providing a hands-on look at the principles discussed in the preceding sections, Accel demonstrates another way that a program can scale its output. Rather than use a non-MM_TEXT mapping mode, it uses CDC::GetDeviceCaps to query the display device for the number of pixels per inch displayed horizontally and vertically. Then it draws each spreadsheet cell so that it's 1 inch wide and ¼ inch tall using raw pixel counts.

Click to view at full size.

Figure 2-13. The Accel window.

Figure 2-14. The Accel application.

Accel.h

#define LINESIZE 8

class CMyApp : public CWinApp
{
public:
    virtual BOOL InitInstance ();
};

class CMainWindow : public CFrameWnd
{
protected:
    int m_nCellWidth;   // Cell width in pixels
    int m_nCellHeight;  // Cell height in pixels
    int m_nRibbonWidth; // Ribbon width in pixels
    int m_nViewWidth;   // Workspace width in pixels
    int m_nViewHeight;  // Workspace height in pixels
    int m_nHScrollPos;  // Horizontal scroll position
    int m_nVScrollPos;  // Vertical scroll position
    int m_nHPageSize;   // Horizontal page size
    int m_nVPageSize;   // Vertical page size

public:
    CMainWindow ();
protected:
    afx_msg void OnPaint ();
    afx_msg int OnCreate (LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSize (UINT nType, int cx, int cy);
    afx_msg void OnHScroll (UINT nCode, UINT nPos, 
        CScrollBar* pScrollBar);
    afx_msg void OnVScroll (UINT nCode, UINT nPos, 
        CScrollBar* pScrollBar);

    DECLARE_MESSAGE_MAP ()
};

Accel.cpp

#include <afxwin.h>
#include "Accel.h"

CMyApp myApp;

/////////////////////////////////////////////////////////////////////////
// CMyApp member functions

BOOL CMyApp::InitInstance ()
{
    m_pMainWnd = new CMainWindow;
    m_pMainWnd->ShowWindow (m_nCmdShow);
    m_pMainWnd->UpdateWindow ();
    return TRUE;
}

/////////////////////////////////////////////////////////////////////////
// CMainWindow message map and member functions

BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
    ON_WM_CREATE ()
    ON_WM_SIZE ()
    ON_WM_PAINT ()
    ON_WM_HSCROLL ()
    ON_WM_VSCROLL ()
END_MESSAGE_MAP ()

CMainWindow::CMainWindow ()
{
    Create (NULL, _T ("Accel"),
        WS_OVERLAPPEDWINDOW ¦ WS_HSCROLL ¦ WS_VSCROLL);
}


int CMainWindow::OnCreate (LPCREATESTRUCT lpCreateStruct)
{

    if (CFrameWnd::OnCreate (lpCreateStruct) == -1)
        return -1;

    CClientDC dc (this);
    m_nCellWidth = dc.GetDeviceCaps (LOGPIXELSX);
    m_nCellHeight = dc.GetDeviceCaps (LOGPIXELSY) / 4;
    m_nRibbonWidth = m_nCellWidth / 2;
    m_nViewWidth = (26 * m_nCellWidth) + m_nRibbonWidth;
    m_nViewHeight = m_nCellHeight * 100;
    return 0;
}

void CMainWindow::OnSize (UINT nType, int cx, int cy)
{
    CFrameWnd::OnSize (nType, cx, cy);

    //
    // Set the horizontal scrolling parameters.
    //
    int nHScrollMax = 0;
    m_nHScrollPos = m_nHPageSize = 0;

    if (cx < m_nViewWidth) {
        nHScrollMax = m_nViewWidth - 1;
        m_nHPageSize = cx;
        m_nHScrollPos = min (m_nHScrollPos, m_nViewWidth -
            m_nHPageSize - 1);
    }


    SCROLLINFO si;
    si.fMask = SIF_PAGE ¦ SIF_RANGE ¦ SIF_POS;
    si.nMin = 0;
    si.nMax = nHScrollMax;
    si.nPos = m_nHScrollPos;
    si.nPage = m_nHPageSize;

    SetScrollInfo (SB_HORZ, &si, TRUE);

    //
    // Set the vertical scrolling parameters.
    //
    int nVScrollMax = 0;
    m_nVScrollPos = m_nVPageSize = 0;

    if (cy < m_nViewHeight) {
        nVScrollMax = m_nViewHeight - 1;
        m_nVPageSize = cy;
        m_nVScrollPos = min (m_nVScrollPos, m_nViewHeight -
            m_nVPageSize - 1);
    }

    si.fMask = SIF_PAGE ¦ SIF_RANGE ¦ SIF_POS;
    si.nMin = 0;
    si.nMax = nVScrollMax;
    si.nPos = m_nVScrollPos;
    si.nPage = m_nVPageSize;

    SetScrollInfo (SB_VERT, &si, TRUE);
}

void CMainWindow::OnPaint ()
{
    CPaintDC dc (this);

    //
    // Set the window origin to reflect the current scroll positions.
    //
    dc.SetWindowOrg (m_nHScrollPos, m_nVScrollPos);

    //
    // Draw the grid lines.
    //


    CPen pen (PS_SOLID, 0, RGB (192, 192, 192));
    CPen* pOldPen = dc.SelectObject (&pen);


    for (int i=0; i<99; i++) {
        int y = (i * m_nCellHeight) + m_nCellHeight;
        dc.MoveTo (0, y);
        dc.LineTo (m_nViewWidth, y);
    }

    for (int j=0; j<26; j++) {
        int x = (j * m_nCellWidth) + m_nRibbonWidth;
        dc.MoveTo (x, 0);
        dc.LineTo (x, m_nViewHeight);
    }

    dc.SelectObject (pOldPen);
    
    //
    // Draw the bodies of the rows and the column headers.
    //
    CBrush brush;
    brush.CreateStockObject (LTGRAY_BRUSH);

    CRect rcTop (0, 0, m_nViewWidth, m_nCellHeight);
    dc.FillRect (rcTop, &brush);
    CRect rcLeft (0, 0, m_nRibbonWidth, m_nViewHeight);
    dc.FillRect (rcLeft, &brush);

    dc.MoveTo (0, m_nCellHeight);
    dc.LineTo (m_nViewWidth, m_nCellHeight);
    dc.MoveTo (m_nRibbonWidth, 0);
    dc.LineTo (m_nRibbonWidth, m_nViewHeight);

    dc.SetBkMode (TRANSPARENT);

    //
    // Add numbers and button outlines to the row headers.
    //
    for (i=0; i<99; i++) {
        int y = (i * m_nCellHeight) + m_nCellHeight;
        dc.MoveTo (0, y);
        dc.LineTo (m_nRibbonWidth, y);

        CString string;
        string.Format (_T ("%d"), i + 1);

        CRect rect (0, y, m_nRibbonWidth, y + m_nCellHeight);
        dc.DrawText (string, &rect, DT_SINGLELINE ¦


            DT_CENTER ¦ DT_VCENTER);

        rect.top++;
        dc.Draw3dRect (rect, RGB (255, 255, 255),
            RGB (128, 128, 128));
    }

    //
    // Add letters and button outlines to the column headers.
    //

    for (j=0; j<26; j++) {
        int x = (j * m_nCellWidth) + m_nRibbonWidth;
        dc.MoveTo (x, 0);
        dc.LineTo (x, m_nCellHeight);

        CString string;
        string.Format (_T ("%c"), j + `A');

        CRect rect (x, 0, x + m_nCellWidth, m_nCellHeight);
        dc.DrawText (string, &rect, DT_SINGLELINE ¦
            DT_CENTER ¦ DT_VCENTER);

        rect.left++;
        dc.Draw3dRect (rect, RGB (255, 255, 255),
            RGB (128, 128, 128));
    }
}

void CMainWindow::OnHScroll (UINT nCode, UINT nPos, CScrollBar* pScrollBar)
{
    int nDelta;

    switch (nCode) {

    case SB_LINELEFT:
        nDelta = -LINESIZE;
        break;

    case SB_PAGELEFT:
        nDelta = -m_nHPageSize;
        break;

    case SB_THUMBTRACK:
        nDelta = (int) nPos - m_nHScrollPos;
        break;

    case SB_PAGERIGHT:
        nDelta = m_nHPageSize;
        break;

    case SB_LINERIGHT:
        nDelta = LINESIZE;
        break;
    default: // Ignore other scroll bar messages
        return;
    }

    int nScrollPos = m_nHScrollPos + nDelta;
    int nMaxPos = m_nViewWidth - m_nHPageSize;

    if (nScrollPos < 0)
        nDelta = -m_nHScrollPos;
    else if (nScrollPos > nMaxPos)
        nDelta = nMaxPos - m_nHScrollPos;

    if (nDelta != 0) {
        m_nHScrollPos += nDelta;
        SetScrollPos (SB_HORZ, m_nHScrollPos, TRUE);
        ScrollWindow (-nDelta, 0);
    }
}

void CMainWindow::OnVScroll (UINT nCode, UINT nPos, CScrollBar* pScrollBar)
{
    int nDelta;

    switch (nCode) {

    case SB_LINEUP:
        nDelta = -LINESIZE;
        break;

    case SB_PAGEUP:
        nDelta = -m_nVPageSize;
        break;

    case SB_THUMBTRACK:
        nDelta = (int) nPos - m_nVScrollPos;
        break;

    case SB_PAGEDOWN:
        nDelta = m_nVPageSize;
        break;

    case SB_LINEDOWN:
        nDelta = LINESIZE;
        break;

    default: // Ignore other scroll bar messages
        return;
    }

    int nScrollPos = m_nVScrollPos + nDelta;
    int nMaxPos = m_nViewHeight - m_nVPageSize;

    if (nScrollPos < 0)
        nDelta = -m_nVScrollPos;
    else if (nScrollPos > nMaxPos)
        nDelta = nMaxPos - m_nVScrollPos;

    if (nDelta != 0) {
        m_nVScrollPos += nDelta;
        SetScrollPos (SB_VERT, m_nVScrollPos, TRUE);
        ScrollWindow (0, -nDelta);
    }
}

GetDeviceCaps is called from CMainWindow's OnCreate handler, which is called upon receipt of a WM_CREATE message. WM_CREATE is the first message a window receives. It is sent just once, and it arrives very early in the window's lifetime—before the window is even visible on the screen. An ON_WM_CREATE entry in the window's message map connects WM_CREATE messages to the member function named OnCreate. OnCreate is the ideal place to initialize member variables whose values can only be determined at run time. It is prototyped as follows:

afx_msg int OnCreate (LPCREATESTRUCT lpCreateStruct)

lpCreateStruct is a pointer to a structure of type CREATESTRUCT, which contains useful information about a window such as its initial size and location on the screen. The value returned by OnCreate determines what Windows does next. If all goes as planned, OnCreate returns 0, signaling to Windows that the window was properly initialized. If OnCreate returns -1, Windows fails the attempt to create the window. A prototype OnCreate handler looks like this:

int CMainWindow::OnCreate (LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate (lpCreateStruct) == -1)
        return -1;

            
      
    return 0;
}

OnCreate should always call the base class's OnCreate handler to give the framework the opportunity to execute its own window-creation code. This is especially important when you write document/view applications, because it is a function called by CFrameWnd::OnCreate that creates the view that goes inside a frame window.

You'll find the code that does the scrolling in the window's OnHScroll and OnVScroll handlers. switch-case logic converts the notification code passed in nCode into a signed nDelta value that represents the number of pixels the window should be scrolled. Once nDelta is computed, the scroll position is adjusted by nDelta pixels and the window is scrolled with the statements

m_nVScrollPos += nDelta;
SetScrollPos (SB_VERT, m_nVScrollPos, TRUE);
ScrollWindow (0, -nDelta);

for the vertical scroll bar and

m_nHScrollPos += nDelta;
SetScrollPos (SB_HORZ, m_nHScrollPos, TRUE);
ScrollWindow (-nDelta, 0);

for the horizontal scroll bar.

How are the scroll positions stored in m_nHScrollPos and m_nVScrollPos factored into the program's output? When OnPaint is called to paint the part of the workspace that was exposed by the scrolling operation, it repositions the window origin with the statement

dc.SetWindowOrg (m_nHScrollPos, m_nVScrollPos);

Recall that CDC::SetWindowOrg tells Windows to map the logical point (x,y) to the device point (0,0), which, for a client-area device context, corresponds to the upper left corner of the window's client area. The statement above moves the origin of the coordinate system left m_nHScrollPos pixels and upward m_nVScrollPos pixels. If OnPaint tries to paint the pixel at (0,0), the coordinate pair is transparently transformed by the GDI into (_m_nHScrollPos,_m_nVScrollPos). If the scroll position is (0,100), the first 100 rows of pixels are clipped from the program's output and the real output—the output the user can see—begins with the 101st row. Repositioning the origin in this manner is a simple and effective way to move a scrollable window over a virtual display surface.

If you could enlarge the window enough to see the entire spreadsheet, you would see the scroll bars disappear. That's because CMainWindow::OnSize sets the scroll bar range to 0 if the window size equals or exceeds the size of the virtual workspace. The OnSize handler also updates the scrolling parameters whenever the window size changes so that the thumb size accurately reflects the relative proportions of the window and the virtual workspace.

And with that, all the pieces are in place. The user clicks a scroll bar or drags a scroll bar thumb; OnHScroll or OnVScroll receives the message and responds by updating the scroll position and scrolling the window; and OnPaint redraws the window, using SetWindowOrg to move the drawing origin an amount that equals the current scroll position. The program's entire workspace is now accessible, despite the physical limitations that the window size imposes on the output. And all for less than 100 additional lines of code. How could it be any easier?

Funny you should ask. Because that's exactly what MFC's CScrollView class is for: to make scrolling easier. CScrollView is an MFC class that encapsulates the behavior of a scrolling window. You tell CScrollView how large a landscape you wish to view, and it handles everything else. Among other things, CScrollView processes WM_VSCROLL and WM_HSCROLL messages for you, scrolls the window in response to scroll bar events, and updates the thumb size when the window size changes.

While it's perfectly possible to wire a CScrollView into an application like Accel, CScrollView was designed primarily for document/view applications. Chapter 10 examines CScrollView more closely and also introduces some of the other view classes that MFC provides.

#include "main.h" #include "gpio.h" #include "MPU6050.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ #define DEBOUNCE_MS 200 // 去抖动时间(ms) #define G_TO_CM_S2 981.0f // 1g = 981 cm/s² #define ACCEL_SENSITIVITY 16384.0f // ±2g量程对应的灵敏度 /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ float current_z_g = 0.0f; // 当前Z轴加速度(g) float current_accel_cm_s2 = 0.0f; float avg_accel = 0.0f; float accel_sum = 0.0f; float accel_cm_s2 = 0.0f; float valid_accel = 0.0f; float Z_THRESHOLD = 0.0f; float initial_z_accel_g = 0.0f; // 初始Z轴加速度值(单位:g) uint32_t last_toggle_time = 0; // 记录上次LED状态切换的时间 float prev_z_g = 0.0f; // 上一次的Z轴加速度值(单位:g) /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ uint8_t ID; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void Blink_Error(uint8_t error_code) { for (int i=0; i<error_code; i++) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_Delay(1000); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_Delay(1000); } HAL_Delay(1000); } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ uint8_t ID; int16_t AX, AY, AZ, GX, GY, GZ; MPU6050_Init(); MPU6050_calibrate(); /* USER CODE BEGIN 2 */ ID = MPU6050_GetID(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ // if(ID == 0xFF) // { // HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // HAL_Delay(50); // } // else // { // HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // HAL_Delay(3000); // } MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); current_z_g = AZ / ACCEL_SENSITIVITY; // 实时更新当前加速度 current_accel_cm_s2 = (current_z_g - initial_z_accel_g) * G_TO_CM_S2; if (current_accel_cm_s2 > Z_THRESHOLD) { // 去抖动处理 if (HAL_GetTick() - last_toggle_time > DEBOUNCE_MS) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); //亮了即触发 last_toggle_time = HAL_GetTick(); } } prev_z_g = current_z_g; // 更新前一次的值 HAL_Delay(50); // 控制检测频率 } /* USER CODE END 3 */ } 以这个代码为基础,开发环境为cubeide和cubemx,将代码修改成实时读取current_z_g和current_accel_cm_s2的代码(不用printf,只需要启动读取,间隔为0.5s)
最新发布
05-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值